diff options
59 files changed, 2 insertions, 26860 deletions
diff --git a/Documentation/isdn/INTERFACE b/Documentation/isdn/INTERFACE deleted file mode 100644 index 5df17e5b25c8..000000000000 --- a/Documentation/isdn/INTERFACE +++ /dev/null | |||
@@ -1,759 +0,0 @@ | |||
1 | $Id: INTERFACE,v 1.15.8.2 2001/03/13 16:17:07 kai Exp $ | ||
2 | |||
3 | Description of the Interface between Linklevel and Hardwarelevel | ||
4 | of isdn4linux: | ||
5 | |||
6 | |||
7 | The Communication between Linklevel (LL) and Hardwarelevel (HL) | ||
8 | is based on the struct isdn_if (defined in isdnif.h). | ||
9 | |||
10 | An HL-driver can register itself at LL by calling the function | ||
11 | register_isdn() with a pointer to that struct. Prior to that, it has | ||
12 | to preset some of the fields of isdn_if. The LL sets the rest of | ||
13 | the fields. All further communication is done via callbacks using | ||
14 | the function-pointers defined in isdn_if. | ||
15 | |||
16 | Changes/Version numbering: | ||
17 | |||
18 | During development of the ISDN subsystem, several changes have been | ||
19 | made to the interface. Before it went into kernel, the package | ||
20 | had a unique version number. The last version, distributed separately | ||
21 | was 0.7.4. When the subsystem went into kernel, every functional unit | ||
22 | got a separate version number. These numbers are shown at initialization, | ||
23 | separated by slashes: | ||
24 | |||
25 | c.c/t.t/n.n/p.p/a.a/v.v | ||
26 | |||
27 | where | ||
28 | |||
29 | c.c is the revision of the common code. | ||
30 | t.t is the revision of the tty related code. | ||
31 | n.n is the revision of the network related code. | ||
32 | p.p is the revision of the ppp related code. | ||
33 | a.a is the revision of the audio related code. | ||
34 | v.v is the revision of the V.110 related code. | ||
35 | |||
36 | Changes in this document are marked with '***CHANGEx' where x representing | ||
37 | the version number. If that number starts with 0, it refers to the old, | ||
38 | separately distributed package. If it starts with one of the letters | ||
39 | above, it refers to the revision of the corresponding module. | ||
40 | ***CHANGEIx refers to the revision number of the isdnif.h | ||
41 | |||
42 | 1. Description of the fields of isdn_if: | ||
43 | |||
44 | int channels; | ||
45 | |||
46 | This field has to be set by the HL-driver to the number of channels | ||
47 | supported prior to calling register_isdn(). Upon return of the call, | ||
48 | the LL puts an id there, which has to be used by the HL-driver when | ||
49 | invoking the other callbacks. | ||
50 | |||
51 | int maxbufsize; | ||
52 | |||
53 | ***CHANGE0.6: New since this version. | ||
54 | |||
55 | Also to be preset by the HL-driver. With this value the HL-driver | ||
56 | tells the LL the maximum size of a data-packet it will accept. | ||
57 | |||
58 | unsigned long features; | ||
59 | |||
60 | To be preset by the HL-driver. Using this field, the HL-driver | ||
61 | announces the features supported. At the moment this is limited to | ||
62 | report the supported layer2 and layer3-protocols. For setting this | ||
63 | field the constants ISDN_FEATURE..., declared in isdnif.h have to be | ||
64 | used. | ||
65 | |||
66 | ***CHANGE0.7.1: The line type (1TR6, EDSS1) has to be set. | ||
67 | |||
68 | unsigned short hl_hdrlen; | ||
69 | |||
70 | ***CHANGE0.7.4: New field. | ||
71 | |||
72 | To be preset by the HL-driver, if it supports sk_buff's. The driver | ||
73 | should put here the amount of additional space needed in sk_buff's for | ||
74 | its internal purposes. Drivers not supporting sk_buff's should | ||
75 | initialize this field to 0. | ||
76 | |||
77 | void (*rcvcallb_skb)(int, int, struct sk_buff *) | ||
78 | |||
79 | ***CHANGE0.7.4: New field. | ||
80 | |||
81 | This field will be set by LL. The HL-driver delivers received data- | ||
82 | packets by calling this function. Upon calling, the HL-driver must | ||
83 | already have its private data pulled off the head of the sk_buff. | ||
84 | |||
85 | Parameter: | ||
86 | int driver-Id | ||
87 | int Channel-number locally to the driver. (starting with 0) | ||
88 | struct sk_buff * Pointer to sk_buff, containing received data. | ||
89 | |||
90 | int (*statcallb)(isdn_ctrl*); | ||
91 | |||
92 | This field will be set by LL. This function has to be called by the | ||
93 | HL-driver for signaling status-changes or other events to the LL. | ||
94 | |||
95 | Parameter: | ||
96 | isdn_ctrl* | ||
97 | |||
98 | The struct isdn_ctrl also defined in isdn_if. The exact meanings of its | ||
99 | fields are described together with the descriptions of the possible | ||
100 | events. Here is only a short description of the fields: | ||
101 | |||
102 | driver = driver Id. | ||
103 | command = event-type. (one of the constants ISDN_STAT_...) | ||
104 | arg = depends on event-type. | ||
105 | num = depends on event-type. | ||
106 | |||
107 | Returnvalue: | ||
108 | 0 on success, else -1 | ||
109 | |||
110 | int (*command)(isdn_ctrl*); | ||
111 | |||
112 | This field has to be preset by the HL-driver. It points to a function, | ||
113 | to be called by LL to perform functions like dialing, B-channel | ||
114 | setup, etc. The exact meaning of the parameters is described with the | ||
115 | descriptions of the possible commands. | ||
116 | |||
117 | Parameter: | ||
118 | isdn_ctrl* | ||
119 | driver = driver-Id | ||
120 | command = command to perform. (one of the constants ISDN_CMD_...) | ||
121 | arg = depends on command. | ||
122 | num = depends on command. | ||
123 | |||
124 | Returnvalue: | ||
125 | >=0 on success, else error-code (-ENODEV etc.) | ||
126 | |||
127 | int (*writebuf_skb)(int, int, int, struct sk_buff *) | ||
128 | |||
129 | ***CHANGE0.7.4: New field. | ||
130 | ***CHANGEI.1.21: New field. | ||
131 | |||
132 | This field has to be preset by the HL-driver. The given function will | ||
133 | be called by the LL for delivering data to be send via B-Channel. | ||
134 | |||
135 | |||
136 | Parameter: | ||
137 | int driver-Id ***CHANGE0.7.4: New parameter. | ||
138 | int channel-number locally to the HL-driver. (starts with 0) | ||
139 | int ack ***ChangeI1.21: New parameter | ||
140 | If this is !0, the driver has to signal the delivery | ||
141 | by sending an ISDN_STAT_BSENT. If this is 0, the driver | ||
142 | MUST NOT send an ISDN_STAT_BSENT. | ||
143 | struct sk_buff * Pointer to sk_buff containing data to be send via | ||
144 | B-channel. | ||
145 | |||
146 | Returnvalue: | ||
147 | Length of data accepted on success, else error-code (-EINVAL on | ||
148 | oversized packets etc.) | ||
149 | |||
150 | int (*writecmd)(u_char*, int, int, int, int); | ||
151 | |||
152 | This field has to be preset by the HL-driver. The given function will be | ||
153 | called to perform write-requests on /dev/isdnctrl (i.e. sending commands | ||
154 | to the card) The data-format is hardware-specific. This function is | ||
155 | intended for debugging only. It is not necessary for normal operation | ||
156 | and never will be called by the tty-emulation- or network-code. If | ||
157 | this function is not supported, the driver has to set NULL here. | ||
158 | |||
159 | Parameter: | ||
160 | u_char* pointer to data. | ||
161 | int length of data. | ||
162 | int flag: 0 = call from within kernel-space. (HL-driver must use | ||
163 | memcpy, may NOT use schedule()) | ||
164 | 1 = call from user-space. (HL-driver must use | ||
165 | memcpy_fromfs, use of schedule() allowed) | ||
166 | int driver-Id. | ||
167 | int channel-number locally to the HL-driver. (starts with 0) | ||
168 | |||
169 | ***CHANGEI1.14: The driver-Id and channel-number are new since this revision. | ||
170 | |||
171 | Returnvalue: | ||
172 | Length of data accepted on success, else error-code (-EINVAL etc.) | ||
173 | |||
174 | int (*readstat)(u_char*, int, int, int, int); | ||
175 | |||
176 | This field has to be preset by the HL-driver. The given function will be | ||
177 | called to perform read-requests on /dev/isdnctrl (i.e. reading replies | ||
178 | from the card) The data-format is hardware-specific. This function is | ||
179 | intended for debugging only. It is not necessary for normal operation | ||
180 | and never will be called by the tty-emulation- or network-code. If | ||
181 | this function is not supported, the driver has to set NULL here. | ||
182 | |||
183 | Parameter: | ||
184 | u_char* pointer to data. | ||
185 | int length of data. | ||
186 | int flag: 0 = call from within kernel-space. (HL-driver must use | ||
187 | memcpy, may NOT use schedule()) | ||
188 | 1 = call from user-space. (HL-driver must use | ||
189 | memcpy_fromfs, use of schedule() allowed) | ||
190 | int driver-Id. | ||
191 | int channel-number locally to the HL-driver. (starts with 0) | ||
192 | |||
193 | ***CHANGEI1.14: The driver-Id and channel-number are new since this revision. | ||
194 | |||
195 | Returnvalue: | ||
196 | Length of data on success, else error-code (-EINVAL etc.) | ||
197 | |||
198 | char id[20]; | ||
199 | ***CHANGE0.7: New since this version. | ||
200 | |||
201 | This string has to be preset by the HL-driver. Its purpose is for | ||
202 | identification of the driver by the user. Eg.: it is shown in the | ||
203 | status-info of /dev/isdninfo. Furthermore it is used as Id for binding | ||
204 | net-interfaces to a specific channel. If a string of length zero is | ||
205 | given, upon return, isdn4linux will replace it by a generic name. (line0, | ||
206 | line1 etc.) It is recommended to make this string configurable during | ||
207 | module-load-time. (copy a global variable to this string.) For doing that, | ||
208 | modules 1.2.8 or newer are necessary. | ||
209 | |||
210 | 2. Description of the commands, a HL-driver has to support: | ||
211 | |||
212 | All commands will be performed by calling the function command() described | ||
213 | above from within the LL. The field command of the struct-parameter will | ||
214 | contain the desired command, the field driver is always set to the | ||
215 | appropriate driver-Id. | ||
216 | |||
217 | Until now, the following commands are defined: | ||
218 | |||
219 | ***CHANGEI1.34: The parameter "num" has been replaced by a union "parm" containing | ||
220 | the old "num" and a new setup_type struct used for ISDN_CMD_DIAL | ||
221 | and ISDN_STAT_ICALL callback. | ||
222 | |||
223 | ISDN_CMD_IOCTL: | ||
224 | |||
225 | This command is intended for performing ioctl-calls for configuring | ||
226 | hardware or similar purposes (setting port-addresses, loading firmware | ||
227 | etc.) For this purpose, in the LL all ioctl-calls with an argument | ||
228 | >= IIOCDRVCTL (0x100) will be handed transparently to this | ||
229 | function after subtracting 0x100 and placing the result in arg. | ||
230 | Example: | ||
231 | If a userlevel-program calls ioctl(0x101,...) the function gets | ||
232 | called with the field command set to 1. | ||
233 | |||
234 | Parameter: | ||
235 | driver = driver-Id. | ||
236 | command = ISDN_CMD_IOCTL | ||
237 | arg = Original ioctl-cmd - IIOCDRVCTL | ||
238 | parm.num = first bytes filled with (unsigned long)arg | ||
239 | |||
240 | Returnvalue: | ||
241 | Depending on driver. | ||
242 | |||
243 | |||
244 | ISDN_CMD_DIAL: | ||
245 | |||
246 | This command is used to tell the HL-driver it should dial a given | ||
247 | number. | ||
248 | |||
249 | Parameter: | ||
250 | driver = driver-Id. | ||
251 | command = ISDN_CMD_DIAL | ||
252 | arg = channel-number locally to the driver. (starting with 0) | ||
253 | |||
254 | parm.setup.phone = An ASCII-String containing the number to dial. | ||
255 | parm.setup.eazmsn = An ASCII-Sting containing the own EAZ or MSN. | ||
256 | parm.setup.si1 = The Service-Indicator. | ||
257 | parm.setup.si2 = Additional Service-Indicator. | ||
258 | |||
259 | If the Line has been designed as SPV (a special german | ||
260 | feature, meaning semi-leased-line) the phone has to | ||
261 | start with an "S". | ||
262 | ***CHANGE0.6: In previous versions the EAZ has been given in the | ||
263 | highbyte of arg. | ||
264 | ***CHANGE0.7.1: New since this version: ServiceIndicator and AddInfo. | ||
265 | |||
266 | ISDN_CMD_ACCEPTD: | ||
267 | |||
268 | With this command, the HL-driver is told to accept a D-Channel-setup. | ||
269 | (Response to an incoming call) | ||
270 | |||
271 | Parameter: | ||
272 | driver = driver-Id. | ||
273 | command = ISDN_CMD_ACCEPTD | ||
274 | arg = channel-number locally to the driver. (starting with 0) | ||
275 | parm = unused. | ||
276 | |||
277 | ISDN_CMD_ACCEPTB: | ||
278 | |||
279 | With this command, the HL-driver is told to perform a B-Channel-setup. | ||
280 | (after establishing D-Channel-Connection) | ||
281 | |||
282 | Parameter: | ||
283 | driver = driver-Id. | ||
284 | command = ISDN_CMD_ACCEPTB | ||
285 | arg = channel-number locally to the driver. (starting with 0) | ||
286 | parm = unused. | ||
287 | |||
288 | ISDN_CMD_HANGUP: | ||
289 | |||
290 | With this command, the HL-driver is told to hangup (B-Channel if | ||
291 | established first, then D-Channel). This command is also used for | ||
292 | actively rejecting an incoming call. | ||
293 | |||
294 | Parameter: | ||
295 | driver = driver-Id. | ||
296 | command = ISDN_CMD_HANGUP | ||
297 | arg = channel-number locally to the driver. (starting with 0) | ||
298 | parm = unused. | ||
299 | |||
300 | ISDN_CMD_CLREAZ: | ||
301 | |||
302 | With this command, the HL-driver is told not to signal incoming | ||
303 | calls to the LL. | ||
304 | |||
305 | Parameter: | ||
306 | driver = driver-Id. | ||
307 | command = ISDN_CMD_CLREAZ | ||
308 | arg = channel-number locally to the driver. (starting with 0) | ||
309 | parm = unused. | ||
310 | |||
311 | ISDN_CMD_SETEAZ: | ||
312 | |||
313 | With this command, the HL-driver is told to signal incoming calls for | ||
314 | the given EAZs/MSNs to the LL. | ||
315 | |||
316 | Parameter: | ||
317 | driver = driver-Id. | ||
318 | command = ISDN_CMD_SETEAZ | ||
319 | arg = channel-number locally to the driver. (starting with 0) | ||
320 | parm.num = ASCII-String, containing the desired EAZ's/MSN's | ||
321 | (comma-separated). If an empty String is given, the | ||
322 | HL-driver should respond to ALL incoming calls, | ||
323 | regardless of the destination-address. | ||
324 | ***CHANGE0.6: New since this version the "empty-string"-feature. | ||
325 | |||
326 | ISDN_CMD_GETEAZ: (currently unused) | ||
327 | |||
328 | With this command, the HL-driver is told to report the current setting | ||
329 | given with ISDN_CMD_SETEAZ. | ||
330 | |||
331 | Parameter: | ||
332 | driver = driver-Id. | ||
333 | command = ISDN_CMD_GETEAZ | ||
334 | arg = channel-number locally to the driver. (starting with 0) | ||
335 | parm.num = ASCII-String, containing the current EAZ's/MSN's | ||
336 | |||
337 | ISDN_CMD_SETSIL: (currently unused) | ||
338 | |||
339 | With this command, the HL-driver is told to signal only incoming | ||
340 | calls with the given Service-Indicators. | ||
341 | |||
342 | Parameter: | ||
343 | driver = driver-Id. | ||
344 | command = ISDN_CMD_SETSIL | ||
345 | arg = channel-number locally to the driver. (starting with 0) | ||
346 | parm.num = ASCII-String, containing the desired Service-Indicators. | ||
347 | |||
348 | ISDN_CMD_GETSIL: (currently unused) | ||
349 | |||
350 | With this command, the HL-driver is told to return the current | ||
351 | Service-Indicators it will respond to. | ||
352 | |||
353 | Parameter: | ||
354 | driver = driver-Id. | ||
355 | command = ISDN_CMD_SETSIL | ||
356 | arg = channel-number locally to the driver. (starting with 0) | ||
357 | parm.num = ASCII-String, containing the current Service-Indicators. | ||
358 | |||
359 | ISDN_CMD_SETL2: | ||
360 | |||
361 | With this command, the HL-driver is told to select the given Layer-2- | ||
362 | protocol. This command is issued by the LL prior to ISDN_CMD_DIAL or | ||
363 | ISDN_CMD_ACCEPTD. | ||
364 | |||
365 | |||
366 | Parameter: | ||
367 | driver = driver-Id. | ||
368 | command = ISDN_CMD_SETL2 | ||
369 | arg = channel-number locally to the driver. (starting with 0) | ||
370 | logical or'ed with (protocol-Id << 8) | ||
371 | protocol-Id is one of the constants ISDN_PROTO_L2... | ||
372 | parm = unused. | ||
373 | |||
374 | ISDN_CMD_GETL2: (currently unused) | ||
375 | |||
376 | With this command, the HL-driver is told to return the current | ||
377 | setting of the Layer-2-protocol. | ||
378 | |||
379 | Parameter: | ||
380 | driver = driver-Id. | ||
381 | command = ISDN_CMD_GETL2 | ||
382 | arg = channel-number locally to the driver. (starting with 0) | ||
383 | parm = unused. | ||
384 | Returnvalue: | ||
385 | current protocol-Id (one of the constants ISDN_L2_PROTO) | ||
386 | |||
387 | ISDN_CMD_SETL3: | ||
388 | |||
389 | With this command, the HL-driver is told to select the given Layer-3- | ||
390 | protocol. This command is issued by the LL prior to ISDN_CMD_DIAL or | ||
391 | ISDN_CMD_ACCEPTD. | ||
392 | |||
393 | |||
394 | Parameter: | ||
395 | driver = driver-Id. | ||
396 | command = ISDN_CMD_SETL3 | ||
397 | arg = channel-number locally to the driver. (starting with 0) | ||
398 | logical or'ed with (protocol-Id << 8) | ||
399 | protocol-Id is one of the constants ISDN_PROTO_L3... | ||
400 | parm.fax = Pointer to T30_s fax struct. (fax usage only) | ||
401 | |||
402 | ISDN_CMD_GETL2: (currently unused) | ||
403 | |||
404 | With this command, the HL-driver is told to return the current | ||
405 | setting of the Layer-3-protocol. | ||
406 | |||
407 | Parameter: | ||
408 | driver = driver-Id. | ||
409 | command = ISDN_CMD_GETL3 | ||
410 | arg = channel-number locally to the driver. (starting with 0) | ||
411 | parm = unused. | ||
412 | Returnvalue: | ||
413 | current protocol-Id (one of the constants ISDN_L3_PROTO) | ||
414 | |||
415 | ISDN_CMD_PROCEED: | ||
416 | |||
417 | With this command, the HL-driver is told to proceed with a incoming call. | ||
418 | |||
419 | Parameter: | ||
420 | driver = driver-Id. | ||
421 | command = ISDN_CMD_PROCEED | ||
422 | arg = channel-number locally to the driver. (starting with 0) | ||
423 | setup.eazmsn= empty string or string send as uus1 in DSS1 with | ||
424 | PROCEED message | ||
425 | |||
426 | ISDN_CMD_ALERT: | ||
427 | |||
428 | With this command, the HL-driver is told to alert a proceeding call. | ||
429 | |||
430 | Parameter: | ||
431 | driver = driver-Id. | ||
432 | command = ISDN_CMD_ALERT | ||
433 | arg = channel-number locally to the driver. (starting with 0) | ||
434 | setup.eazmsn= empty string or string send as uus1 in DSS1 with | ||
435 | ALERT message | ||
436 | |||
437 | ISDN_CMD_REDIR: | ||
438 | |||
439 | With this command, the HL-driver is told to redirect a call in proceeding | ||
440 | or alerting state. | ||
441 | |||
442 | Parameter: | ||
443 | driver = driver-Id. | ||
444 | command = ISDN_CMD_REDIR | ||
445 | arg = channel-number locally to the driver. (starting with 0) | ||
446 | setup.eazmsn= empty string or string send as uus1 in DSS1 protocol | ||
447 | setup.screen= screening indicator | ||
448 | setup.phone = redirected to party number | ||
449 | |||
450 | ISDN_CMD_PROT_IO: | ||
451 | |||
452 | With this call, the LL-driver invokes protocol specific features through | ||
453 | the LL. | ||
454 | The call is not implicitely bound to a connection. | ||
455 | |||
456 | Parameter: | ||
457 | driver = driver-Id | ||
458 | command = ISDN_CMD_PROT_IO | ||
459 | arg = The lower 8 Bits define the addressed protocol as defined | ||
460 | in ISDN_PTYPE..., the upper bits are used to differentiate | ||
461 | the protocol specific CMD. | ||
462 | |||
463 | para = protocol and function specific. See isdnif.h for detail. | ||
464 | |||
465 | |||
466 | ISDN_CMD_FAXCMD: | ||
467 | |||
468 | With this command the HL-driver receives a fax sub-command. | ||
469 | For details refer to INTERFACE.fax | ||
470 | |||
471 | Parameter: | ||
472 | driver = driver-Id. | ||
473 | command = ISDN_CMD_FAXCMD | ||
474 | arg = channel-number locally to the driver. (starting with 0) | ||
475 | parm = unused. | ||
476 | |||
477 | |||
478 | 3. Description of the events to be signaled by the HL-driver to the LL. | ||
479 | |||
480 | All status-changes are signaled via calling the previously described | ||
481 | function statcallb(). The field command of the struct isdn_cmd has | ||
482 | to be set by the HL-driver with the appropriate Status-Id (event-number). | ||
483 | The field arg has to be set to the channel-number (locally to the driver, | ||
484 | starting with 0) to which this event applies. (Exception: STAVAIL-event) | ||
485 | |||
486 | Until now, the following Status-Ids are defined: | ||
487 | |||
488 | ISDN_STAT_AVAIL: | ||
489 | |||
490 | With this call, the HL-driver signals the availability of new data | ||
491 | for readstat(). Used only for debugging-purposes, see description | ||
492 | of readstat(). | ||
493 | |||
494 | Parameter: | ||
495 | driver = driver-Id | ||
496 | command = ISDN_STAT_STAVAIL | ||
497 | arg = length of available data. | ||
498 | parm = unused. | ||
499 | |||
500 | ISDN_STAT_ICALL: | ||
501 | ISDN_STAT_ICALLW: | ||
502 | |||
503 | With this call, the HL-driver signals an incoming call to the LL. | ||
504 | If ICALLW is signalled the incoming call is a waiting call without | ||
505 | a available B-chan. | ||
506 | |||
507 | Parameter: | ||
508 | driver = driver-Id | ||
509 | command = ISDN_STAT_ICALL | ||
510 | arg = channel-number, locally to the driver. (starting with 0) | ||
511 | para.setup.phone = Callernumber. | ||
512 | para.setup.eazmsn = CalledNumber. | ||
513 | para.setup.si1 = Service Indicator. | ||
514 | para.setup.si2 = Additional Service Indicator. | ||
515 | para.setup.plan = octet 3 from Calling party number Information Element. | ||
516 | para.setup.screen = octet 3a from Calling party number Information Element. | ||
517 | |||
518 | Return: | ||
519 | 0 = No device matching this call. | ||
520 | 1 = At least one device matching this call (RING on ttyI). | ||
521 | HL-driver may send ALERTING on the D-channel in this case. | ||
522 | 2 = Call will be rejected. | ||
523 | 3 = Incoming called party number is currently incomplete. | ||
524 | Additional digits are required. | ||
525 | Used for signalling with PtP connections. | ||
526 | 4 = Call will be held in a proceeding state | ||
527 | (HL driver sends PROCEEDING) | ||
528 | Used when a user space prog needs time to interpret a call | ||
529 | para.setup.eazmsn may be filled with an uus1 message of | ||
530 | 30 octets maximum. Empty string if no uus. | ||
531 | 5 = Call will be actively deflected to another party | ||
532 | Only available in DSS1/EURO protocol | ||
533 | para.setup.phone must be set to destination party number | ||
534 | para.setup.eazmsn may be filled with an uus1 message of | ||
535 | 30 octets maximum. Empty string if no uus. | ||
536 | -1 = An error happened. (Invalid parameters for example.) | ||
537 | The keypad support now is included in the dial command. | ||
538 | |||
539 | |||
540 | ISDN_STAT_RUN: | ||
541 | |||
542 | With this call, the HL-driver signals availability of the ISDN-card. | ||
543 | (after initializing, loading firmware) | ||
544 | |||
545 | Parameter: | ||
546 | driver = driver-Id | ||
547 | command = ISDN_STAT_RUN | ||
548 | arg = unused. | ||
549 | parm = unused. | ||
550 | |||
551 | ISDN_STAT_STOP: | ||
552 | |||
553 | With this call, the HL-driver signals unavailability of the ISDN-card. | ||
554 | (before unloading, while resetting/reconfiguring the card) | ||
555 | |||
556 | Parameter: | ||
557 | driver = driver-Id | ||
558 | command = ISDN_STAT_STOP | ||
559 | arg = unused. | ||
560 | parm = unused. | ||
561 | |||
562 | ISDN_STAT_DCONN: | ||
563 | |||
564 | With this call, the HL-driver signals the successful establishment of | ||
565 | a D-Channel-connection. (Response to ISDN_CMD_ACCEPTD or ISDN_CMD_DIAL) | ||
566 | |||
567 | Parameter: | ||
568 | driver = driver-Id | ||
569 | command = ISDN_STAT_DCONN | ||
570 | arg = channel-number, locally to the driver. (starting with 0) | ||
571 | parm = unused. | ||
572 | |||
573 | ISDN_STAT_BCONN: | ||
574 | |||
575 | With this call, the HL-driver signals the successful establishment of | ||
576 | a B-Channel-connection. (Response to ISDN_CMD_ACCEPTB or because the | ||
577 | remote-station has initiated establishment) | ||
578 | |||
579 | The HL driver should call this when the logical l2/l3 protocol | ||
580 | connection on top of the physical B-channel is established. | ||
581 | |||
582 | Parameter: | ||
583 | driver = driver-Id | ||
584 | command = ISDN_STAT_BCONN | ||
585 | arg = channel-number, locally to the driver. (starting with 0) | ||
586 | parm.num = ASCII-String, containing type of connection (for analog | ||
587 | modem only). This will be appended to the CONNECT message | ||
588 | e.g. 14400/V.32bis | ||
589 | |||
590 | ISDN_STAT_DHUP: | ||
591 | |||
592 | With this call, the HL-driver signals the shutdown of a | ||
593 | D-Channel-connection. This could be a response to a prior ISDN_CMD_HANGUP, | ||
594 | or caused by a remote-hangup or if the remote-station has actively | ||
595 | rejected a call. | ||
596 | |||
597 | Parameter: | ||
598 | driver = driver-Id | ||
599 | command = ISDN_STAT_DHUP | ||
600 | arg = channel-number, locally to the driver. (starting with 0) | ||
601 | parm = unused. | ||
602 | |||
603 | ISDN_STAT_BHUP: | ||
604 | |||
605 | With this call, the HL-driver signals the shutdown of a | ||
606 | B-Channel-connection. This could be a response to a prior ISDN_CMD_HANGUP, | ||
607 | or caused by a remote-hangup. | ||
608 | |||
609 | The HL driver should call this as soon as the logical l2/l3 protocol | ||
610 | connection on top of the physical B-channel is released. | ||
611 | |||
612 | Parameter: | ||
613 | driver = driver-Id | ||
614 | command = ISDN_STAT_BHUP | ||
615 | arg = channel-number, locally to the driver. (starting with 0) | ||
616 | parm = unused. | ||
617 | |||
618 | ISDN_STAT_CINF: | ||
619 | |||
620 | With this call, the HL-driver delivers charge-unit information to the | ||
621 | LL. | ||
622 | |||
623 | Parameter: | ||
624 | driver = driver-Id | ||
625 | command = ISDN_STAT_CINF | ||
626 | arg = channel-number, locally to the driver. (starting with 0) | ||
627 | parm.num = ASCII string containing charge-units (digits only). | ||
628 | |||
629 | ISDN_STAT_LOAD: (currently unused) | ||
630 | |||
631 | ISDN_STAT_UNLOAD: | ||
632 | |||
633 | With this call, the HL-driver signals that it will be unloaded now. This | ||
634 | tells the LL to release all corresponding data-structures. | ||
635 | |||
636 | Parameter: | ||
637 | driver = driver-Id | ||
638 | command = ISDN_STAT_UNLOAD | ||
639 | arg = unused. | ||
640 | parm = unused. | ||
641 | |||
642 | ISDN_STAT_BSENT: | ||
643 | |||
644 | With this call the HL-driver signals the delivery of a data-packet. | ||
645 | This callback is used by the network-interfaces only, tty-Emulation | ||
646 | does not need this call. | ||
647 | |||
648 | Parameter: | ||
649 | driver = driver-Id | ||
650 | command = ISDN_STAT_BSENT | ||
651 | arg = channel-number, locally to the driver. (starting with 0) | ||
652 | parm.length = ***CHANGEI.1.21: New field. | ||
653 | the driver has to set this to the original length | ||
654 | of the skb at the time of receiving it from the linklevel. | ||
655 | |||
656 | ISDN_STAT_NODCH: | ||
657 | |||
658 | With this call, the driver has to respond to a prior ISDN_CMD_DIAL, if | ||
659 | no D-Channel is available. | ||
660 | |||
661 | Parameter: | ||
662 | driver = driver-Id | ||
663 | command = ISDN_STAT_NODCH | ||
664 | arg = channel-number, locally to the driver. (starting with 0) | ||
665 | parm = unused. | ||
666 | |||
667 | ISDN_STAT_ADDCH: | ||
668 | |||
669 | This call is for HL-drivers, which are unable to check card-type | ||
670 | or numbers of supported channels before they have loaded any firmware | ||
671 | using ioctl. Those HL-driver simply set the channel-parameter to a | ||
672 | minimum channel-number when registering, and later if they know | ||
673 | the real amount, perform this call, allocating additional channels. | ||
674 | |||
675 | Parameter: | ||
676 | driver = driver-Id | ||
677 | command = ISDN_STAT_ADDCH | ||
678 | arg = number of channels to be added. | ||
679 | parm = unused. | ||
680 | |||
681 | ISDN_STAT_CAUSE: | ||
682 | |||
683 | With this call, the HL-driver delivers CAUSE-messages to the LL. | ||
684 | Currently the LL does not use this messages. Their contents is simply | ||
685 | logged via kernel-messages. Therefore, currently the format of the | ||
686 | messages is completely free. However they should be printable. | ||
687 | |||
688 | Parameter: | ||
689 | driver = driver-Id | ||
690 | command = ISDN_STAT_NODCH | ||
691 | arg = channel-number, locally to the driver. (starting with 0) | ||
692 | parm.num = ASCII string containing CAUSE-message. | ||
693 | |||
694 | ISDN_STAT_DISPLAY: | ||
695 | |||
696 | With this call, the HL-driver delivers DISPLAY-messages to the LL. | ||
697 | Currently the LL does not use this messages. | ||
698 | |||
699 | Parameter: | ||
700 | driver = driver-Id | ||
701 | command = ISDN_STAT_DISPLAY | ||
702 | arg = channel-number, locally to the driver. (starting with 0) | ||
703 | para.display= string containing DISPLAY-message. | ||
704 | |||
705 | ISDN_STAT_PROT: | ||
706 | |||
707 | With this call, the HL-driver delivers protocol specific infos to the LL. | ||
708 | The call is not implicitely bound to a connection. | ||
709 | |||
710 | Parameter: | ||
711 | driver = driver-Id | ||
712 | command = ISDN_STAT_PROT | ||
713 | arg = The lower 8 Bits define the addressed protocol as defined | ||
714 | in ISDN_PTYPE..., the upper bits are used to differentiate | ||
715 | the protocol specific STAT. | ||
716 | |||
717 | para = protocol and function specific. See isdnif.h for detail. | ||
718 | |||
719 | ISDN_STAT_DISCH: | ||
720 | |||
721 | With this call, the HL-driver signals the LL to disable or enable the | ||
722 | use of supplied channel and driver. | ||
723 | The call may be used to reduce the available number of B-channels after | ||
724 | loading the driver. The LL has to ignore a disabled channel when searching | ||
725 | for free channels. The HL driver itself never delivers STAT callbacks for | ||
726 | disabled channels. | ||
727 | The LL returns a nonzero code if the operation was not successful or the | ||
728 | selected channel is actually regarded as busy. | ||
729 | |||
730 | Parameter: | ||
731 | driver = driver-Id | ||
732 | command = ISDN_STAT_DISCH | ||
733 | arg = channel-number, locally to the driver. (starting with 0) | ||
734 | parm.num[0] = 0 if channel shall be disabled, else enabled. | ||
735 | |||
736 | ISDN_STAT_L1ERR: | ||
737 | |||
738 | ***CHANGEI1.21 new status message. | ||
739 | A signal can be sent to the linklevel if an Layer1-error results in | ||
740 | packet-loss on receive or send. The field errcode of the cmd.parm | ||
741 | union describes the error more precisely. | ||
742 | |||
743 | Parameter: | ||
744 | driver = driver-Id | ||
745 | command = ISDN_STAT_L1ERR | ||
746 | arg = channel-number, locally to the driver. (starting with 0) | ||
747 | parm.errcode= ISDN_STAT_L1ERR_SEND: Packet lost while sending. | ||
748 | ISDN_STAT_L1ERR_RECV: Packet lost while receiving. | ||
749 | ISDN_STAT_FAXIND: | ||
750 | |||
751 | With this call the HL-driver signals a fax sub-command to the LL. | ||
752 | For details refer to INTERFACE.fax | ||
753 | |||
754 | Parameter: | ||
755 | driver = driver-Id. | ||
756 | command = ISDN_STAT_FAXIND | ||
757 | arg = channel-number, locally to the driver. (starting with 0) | ||
758 | parm = unused. | ||
759 | |||
diff --git a/Documentation/isdn/INTERFACE.fax b/Documentation/isdn/INTERFACE.fax deleted file mode 100644 index 9c8c6d914ec7..000000000000 --- a/Documentation/isdn/INTERFACE.fax +++ /dev/null | |||
@@ -1,163 +0,0 @@ | |||
1 | $Id: INTERFACE.fax,v 1.2 2000/08/06 09:22:50 armin Exp $ | ||
2 | |||
3 | |||
4 | Description of the fax-subinterface between linklevel and hardwarelevel of | ||
5 | isdn4linux. | ||
6 | |||
7 | The communication between linklevel (LL) and hardwarelevel (HL) for fax | ||
8 | is based on the struct T30_s (defined in isdnif.h). | ||
9 | This struct is allocated in the LL. | ||
10 | In order to use fax, the LL provides the pointer to this struct with the | ||
11 | command ISDN_CMD_SETL3 (parm.fax). This pointer expires in case of hangup | ||
12 | and when a new channel to a new connection is assigned. | ||
13 | |||
14 | |||
15 | Data handling: | ||
16 | In send-mode the HL-driver has to handle the <DLE> codes and the bit-order | ||
17 | conversion by itself. | ||
18 | In receive-mode the LL-driver takes care of the bit-order conversion | ||
19 | (specified by +FBOR) | ||
20 | |||
21 | Structure T30_s description: | ||
22 | |||
23 | This structure stores the values (set by AT-commands), the remote- | ||
24 | capability-values and the command-codes between LL and HL. | ||
25 | |||
26 | If the HL-driver receives ISDN_CMD_FAXCMD, all needed information | ||
27 | is in this struct set by the LL. | ||
28 | To signal information to the LL, the HL-driver has to set the | ||
29 | parameters and use ISDN_STAT_FAXIND. | ||
30 | (Please refer to INTERFACE) | ||
31 | |||
32 | Structure T30_s: | ||
33 | |||
34 | All members are 8-bit unsigned (__u8) | ||
35 | |||
36 | - resolution | ||
37 | - rate | ||
38 | - width | ||
39 | - length | ||
40 | - compression | ||
41 | - ecm | ||
42 | - binary | ||
43 | - scantime | ||
44 | - id[] | ||
45 | Local faxmachine's parameters, set by +FDIS, +FDCS, +FLID, ... | ||
46 | |||
47 | - r_resolution | ||
48 | - r_rate | ||
49 | - r_width | ||
50 | - r_length | ||
51 | - r_compression | ||
52 | - r_ecm | ||
53 | - r_binary | ||
54 | - r_scantime | ||
55 | - r_id[] | ||
56 | Remote faxmachine's parameters. To be set by HL-driver. | ||
57 | |||
58 | - phase | ||
59 | Defines the actual state of fax connection. Set by HL or LL | ||
60 | depending on progress and type of connection. | ||
61 | If the phase changes because of an AT command, the LL driver | ||
62 | changes this value. Otherwise the HL-driver takes care of it, but | ||
63 | only necessary on call establishment (from IDLE to PHASE_A). | ||
64 | (one of the constants ISDN_FAX_PHASE_[IDLE,A,B,C,D,E]) | ||
65 | |||
66 | - direction | ||
67 | Defines outgoing/send or incoming/receive connection. | ||
68 | (ISDN_TTY_FAX_CONN_[IN,OUT]) | ||
69 | |||
70 | - code | ||
71 | Commands from LL to HL; possible constants : | ||
72 | ISDN_TTY_FAX_DR signals +FDR command to HL | ||
73 | |||
74 | ISDN_TTY_FAX_DT signals +FDT command to HL | ||
75 | |||
76 | ISDN_TTY_FAX_ET signals +FET command to HL | ||
77 | |||
78 | |||
79 | Other than that the "code" is set with the hangup-code value at | ||
80 | the end of connection for the +FHNG message. | ||
81 | |||
82 | - r_code | ||
83 | Commands from HL to LL; possible constants : | ||
84 | ISDN_TTY_FAX_CFR output of +FCFR message. | ||
85 | |||
86 | ISDN_TTY_FAX_RID output of remote ID set in r_id[] | ||
87 | (+FCSI/+FTSI on send/receive) | ||
88 | |||
89 | ISDN_TTY_FAX_DCS output of +FDCS and CONNECT message, | ||
90 | switching to phase C. | ||
91 | |||
92 | ISDN_TTY_FAX_ET signals end of data, | ||
93 | switching to phase D. | ||
94 | |||
95 | ISDN_TTY_FAX_FCON signals the established, outgoing connection, | ||
96 | switching to phase B. | ||
97 | |||
98 | ISDN_TTY_FAX_FCON_I signals the established, incoming connection, | ||
99 | switching to phase B. | ||
100 | |||
101 | ISDN_TTY_FAX_DIS output of +FDIS message and values. | ||
102 | |||
103 | ISDN_TTY_FAX_SENT signals that all data has been sent | ||
104 | and <DLE><ETX> is acknowledged, | ||
105 | OK message will be sent. | ||
106 | |||
107 | ISDN_TTY_FAX_PTS signals a msg-confirmation (page sent successful), | ||
108 | depending on fet value: | ||
109 | 0: output OK message (more pages follow) | ||
110 | 1: switching to phase B (next document) | ||
111 | |||
112 | ISDN_TTY_FAX_TRAIN_OK output of +FDCS and OK message (for receive mode). | ||
113 | |||
114 | ISDN_TTY_FAX_EOP signals end of data in receive mode, | ||
115 | switching to phase D. | ||
116 | |||
117 | ISDN_TTY_FAX_HNG output of the +FHNG and value set by code and | ||
118 | OK message, switching to phase E. | ||
119 | |||
120 | |||
121 | - badlin | ||
122 | Value of +FBADLIN | ||
123 | |||
124 | - badmul | ||
125 | Value of +FBADMUL | ||
126 | |||
127 | - bor | ||
128 | Value of +FBOR | ||
129 | |||
130 | - fet | ||
131 | Value of +FET command in send-mode. | ||
132 | Set by HL in receive-mode for +FET message. | ||
133 | |||
134 | - pollid[] | ||
135 | ID-string, set by +FCIG | ||
136 | |||
137 | - cq | ||
138 | Value of +FCQ | ||
139 | |||
140 | - cr | ||
141 | Value of +FCR | ||
142 | |||
143 | - ctcrty | ||
144 | Value of +FCTCRTY | ||
145 | |||
146 | - minsp | ||
147 | Value of +FMINSP | ||
148 | |||
149 | - phcto | ||
150 | Value of +FPHCTO | ||
151 | |||
152 | - rel | ||
153 | Value of +FREL | ||
154 | |||
155 | - nbc | ||
156 | Value of +FNBC (0,1) | ||
157 | (+FNBC is not a known class 2 fax command, I added this to change the | ||
158 | automatic "best capabilities" connection in the eicon HL-driver) | ||
159 | |||
160 | |||
161 | Armin | ||
162 | mac@melware.de | ||
163 | |||
diff --git a/Documentation/isdn/README b/Documentation/isdn/README deleted file mode 100644 index 74bd2bdb455b..000000000000 --- a/Documentation/isdn/README +++ /dev/null | |||
@@ -1,599 +0,0 @@ | |||
1 | README for the ISDN-subsystem | ||
2 | |||
3 | 1. Preface | ||
4 | |||
5 | 1.1 Introduction | ||
6 | |||
7 | This README describes how to set up and how to use the different parts | ||
8 | of the ISDN-subsystem. | ||
9 | |||
10 | For using the ISDN-subsystem, some additional userlevel programs are | ||
11 | necessary. Those programs and some contributed utilities are available | ||
12 | at | ||
13 | |||
14 | ftp.isdn4linux.de | ||
15 | |||
16 | /pub/isdn4linux/isdn4k-utils-<VersionNumber>.tar.gz | ||
17 | |||
18 | |||
19 | We also have set up a mailing-list: | ||
20 | |||
21 | The isdn4linux-project originates in Germany, and therefore by historical | ||
22 | reasons, the mailing-list's primary language is german. However mails | ||
23 | written in english have been welcome all the time. | ||
24 | |||
25 | to subscribe: write a email to majordomo@listserv.isdn4linux.de, | ||
26 | Subject irrelevant, in the message body: | ||
27 | subscribe isdn4linux <your_email_address> | ||
28 | |||
29 | To write to the mailing-list, write to isdn4linux@listserv.isdn4linux.de | ||
30 | |||
31 | This mailinglist is bidirectionally gated to the newsgroup | ||
32 | |||
33 | de.alt.comm.isdn4linux | ||
34 | |||
35 | There is also a well maintained FAQ in English available at | ||
36 | https://www.mhessler.de/i4lfaq/ | ||
37 | It can be viewed online, or downloaded in sgml/text/html format. | ||
38 | The FAQ can also be viewed online at | ||
39 | https://www.isdn4linux.de/faq/i4lfaq.html | ||
40 | or downloaded from | ||
41 | ftp://ftp.isdn4linux.de/pub/isdn4linux/FAQ/ | ||
42 | |||
43 | 1.1 Technical details | ||
44 | |||
45 | In the following Text, the terms MSN and EAZ are used. | ||
46 | |||
47 | MSN is the abbreviation for (M)ultiple(S)ubscriber(N)umber, and applies | ||
48 | to Euro(EDSS1)-type lines. Usually it is simply the phone number. | ||
49 | |||
50 | EAZ is the abbreviation of (E)ndgeraete(A)uswahl(Z)iffer and | ||
51 | applies to German 1TR6-type lines. This is a one-digit string, | ||
52 | simply appended to the base phone number | ||
53 | |||
54 | The internal handling is nearly identical, so replace the appropriate | ||
55 | term to that one, which applies to your local ISDN-environment. | ||
56 | |||
57 | When the link-level-module isdn.o is loaded, it supports up to 16 | ||
58 | low-level-modules with up to 64 channels. (The number 64 is arbitrarily | ||
59 | chosen and can be configured at compile-time --ISDN_MAX in isdn.h). | ||
60 | A low-level-driver can register itself through an interface (which is | ||
61 | defined in isdnif.h) and gets assigned a slot. | ||
62 | The following char-devices are made available for each channel: | ||
63 | |||
64 | A raw-control-device with the following functions: | ||
65 | write: raw D-channel-messages (format: depends on driver). | ||
66 | read: raw D-channel-messages (format: depends on driver). | ||
67 | ioctl: depends on driver, i.e. for the ICN-driver, the base-address of | ||
68 | the ports and the shared memory on the card can be set and read | ||
69 | also the boot-code and the protocol software can be loaded into | ||
70 | the card. | ||
71 | |||
72 | O N L Y !!! for debugging (no locking against other devices): | ||
73 | One raw-data-device with the following functions: | ||
74 | write: data to B-channel. | ||
75 | read: data from B-channel. | ||
76 | |||
77 | In addition the following devices are made available: | ||
78 | |||
79 | 128 tty-devices (64 cuix and 64 ttyIx) with integrated modem-emulator: | ||
80 | The functionality is almost the same as that of a serial device | ||
81 | (the line-discs are handled by the kernel), which lets you run | ||
82 | SLIP, CSLIP and asynchronous PPP through the devices. We have tested | ||
83 | Seyon, minicom, CSLIP (uri-dip) PPP, mgetty, XCept and Hylafax. | ||
84 | |||
85 | The modem-emulation supports the following: | ||
86 | 1.3.1 Commands: | ||
87 | |||
88 | ATA Answer incoming call. | ||
89 | ATD<No.> Dial, the number may contain: | ||
90 | [0-9] and [,#.*WPT-S] | ||
91 | the latter are ignored until 'S'. | ||
92 | The 'S' must precede the number, if | ||
93 | the line is a SPV (German 1TR6). | ||
94 | ATE0 Echo off. | ||
95 | ATE1 Echo on (default). | ||
96 | ATH Hang-up. | ||
97 | ATH1 Off hook (ignored). | ||
98 | ATH0 Hang-up. | ||
99 | ATI Return "ISDN for Linux...". | ||
100 | ATI0 " | ||
101 | ATI1 " | ||
102 | ATI2 Report of last connection. | ||
103 | ATO On line (data mode). | ||
104 | ATQ0 Enable result codes (default). | ||
105 | ATQ1 Disable result codes (default). | ||
106 | ATSx=y Set register x to y. | ||
107 | ATSx? Show contents of register x. | ||
108 | ATV0 Numeric responses. | ||
109 | ATV1 English responses (default). | ||
110 | ATZ Load registers and EAZ/MSN from Profile. | ||
111 | AT&Bx Set Send-Packet-size to x (max. 4000) | ||
112 | The real packet-size may be limited by the | ||
113 | low-level-driver used. e.g. the HiSax-Module- | ||
114 | limit is 2000. You will get NO Error-Message, | ||
115 | if you set it to higher values, because at the | ||
116 | time of giving this command the corresponding | ||
117 | driver may not be selected (see "Automatic | ||
118 | Assignment") however the size of outgoing packets | ||
119 | will be limited correctly. | ||
120 | AT&D0 Ignore DTR | ||
121 | AT&D2 DTR-low-edge: Hang up and return to | ||
122 | command mode (default). | ||
123 | AT&D3 Same as AT&D2 but also resets all registers. | ||
124 | AT&Ex Set the EAZ/MSN for this channel to x. | ||
125 | AT&F Reset all registers and profile to "factory-defaults" | ||
126 | AT&Lx Set list of phone numbers to listen on. x is a | ||
127 | list of wildcard patterns separated by semicolon. | ||
128 | If this is set, it has precedence over the MSN set | ||
129 | by AT&E. | ||
130 | AT&Rx Select V.110 bitrate adaption. | ||
131 | This command enables V.110 protocol with 9600 baud | ||
132 | (x=9600), 19200 baud (x=19200) or 38400 baud | ||
133 | (x=38400). A value of x=0 disables V.110 switching | ||
134 | back to default X.75. This command sets the following | ||
135 | Registers: | ||
136 | Reg 14 (Layer-2 protocol): | ||
137 | x = 0: 0 | ||
138 | x = 9600: 7 | ||
139 | x = 19200: 8 | ||
140 | x = 38400: 9 | ||
141 | Reg 18.2 = 1 | ||
142 | Reg 19 (Additional Service Indicator): | ||
143 | x = 0: 0 | ||
144 | x = 9600: 197 | ||
145 | x = 19200: 199 | ||
146 | x = 38400: 198 | ||
147 | Note on value in Reg 19: | ||
148 | There is _NO_ common convention for 38400 baud. | ||
149 | The value 198 is chosen arbitrarily. Users | ||
150 | _MUST_ negotiate this value before establishing | ||
151 | a connection. | ||
152 | AT&Sx Set window-size (x = 1..8) (not yet implemented) | ||
153 | AT&V Show all settings. | ||
154 | AT&W0 Write registers and EAZ/MSN to profile. See also | ||
155 | iprofd (5.c in this README). | ||
156 | AT&X0 BTX-mode and T.70-mode off (default) | ||
157 | AT&X1 BTX-mode on. (S13.1=1, S13.5=0 S14=0, S16=7, S18=7, S19=0) | ||
158 | AT&X2 T.70-mode on. (S13.1=1, S13.5=1, S14=0, S16=7, S18=7, S19=0) | ||
159 | AT+Rx Resume a suspended call with CallID x (x = 1,2,3...) | ||
160 | AT+Sx Suspend a call with CallID x (x = 1,2,3...) | ||
161 | |||
162 | For voice-mode commands refer to README.audio | ||
163 | |||
164 | 1.3.2 Escape sequence: | ||
165 | During a connection, the emulation reacts just like | ||
166 | a normal modem to the escape sequence <DELAY>+++<DELAY>. | ||
167 | (The escape character - default '+' - can be set in the | ||
168 | register 2). | ||
169 | The DELAY must at least be 1.5 seconds long and delay | ||
170 | between the escape characters must not exceed 0.5 seconds. | ||
171 | |||
172 | 1.3.3 Registers: | ||
173 | |||
174 | Nr. Default Description | ||
175 | 0 0 Answer on ring number. | ||
176 | (no auto-answer if S0=0). | ||
177 | 1 0 Count of rings. | ||
178 | 2 43 Escape character. | ||
179 | (a value >= 128 disables the escape sequence). | ||
180 | 3 13 Carriage return character (ASCII). | ||
181 | 4 10 Line feed character (ASCII). | ||
182 | 5 8 Backspace character (ASCII). | ||
183 | 6 3 Delay in seconds before dialing. | ||
184 | 7 60 Wait for carrier. | ||
185 | 8 2 Pause time for comma (ignored) | ||
186 | 9 6 Carrier detect time (ignored) | ||
187 | 10 7 Carrier loss to disconnect time (ignored). | ||
188 | 11 70 Touch tone timing (ignored). | ||
189 | 12 69 Bit coded register: | ||
190 | Bit 0: 0 = Suppress response messages. | ||
191 | 1 = Show response messages. | ||
192 | Bit 1: 0 = English response messages. | ||
193 | 1 = Numeric response messages. | ||
194 | Bit 2: 0 = Echo off. | ||
195 | 1 = Echo on. | ||
196 | Bit 3 0 = DCD always on. | ||
197 | 1 = DCD follows carrier. | ||
198 | Bit 4 0 = CTS follows RTS | ||
199 | 1 = Ignore RTS, CTS always on. | ||
200 | Bit 5 0 = return to command mode on DTR low. | ||
201 | 1 = Same as 0 but also resets all | ||
202 | registers. | ||
203 | See also register 13, bit 2 | ||
204 | Bit 6 0 = DSR always on. | ||
205 | 1 = DSR only on if channel is available. | ||
206 | Bit 7 0 = Cisco-PPP-flag-hack off (default). | ||
207 | 1 = Cisco-PPP-flag-hack on. | ||
208 | 13 0 Bit coded register: | ||
209 | Bit 0: 0 = Use delayed tty-send-algorithm | ||
210 | 1 = Direct tty-send. | ||
211 | Bit 1: 0 = T.70 protocol (Only for BTX!) off | ||
212 | 1 = T.70 protocol (Only for BTX!) on | ||
213 | Bit 2: 0 = Don't hangup on DTR low. | ||
214 | 1 = Hangup on DTR low. | ||
215 | Bit 3: 0 = Standard response messages | ||
216 | 1 = Extended response messages | ||
217 | Bit 4: 0 = CALLER NUMBER before every RING. | ||
218 | 1 = CALLER NUMBER after first RING. | ||
219 | Bit 5: 0 = T.70 extended protocol off | ||
220 | 1 = T.70 extended protocol on | ||
221 | Bit 6: 0 = Special RUNG Message off | ||
222 | 1 = Special RUNG Message on | ||
223 | "RUNG" is delivered on a ttyI, if | ||
224 | an incoming call happened (RING) and | ||
225 | the remote party hung up before any | ||
226 | local ATA was given. | ||
227 | Bit 7: 0 = Don't show display messages from net | ||
228 | 1 = Show display messages from net | ||
229 | (S12 Bit 1 must be 0 too) | ||
230 | 14 0 Layer-2 protocol: | ||
231 | 0 = X75/LAPB with I-frames | ||
232 | 1 = X75/LAPB with UI-frames | ||
233 | 2 = X75/LAPB with BUI-frames | ||
234 | 3 = HDLC | ||
235 | 4 = Transparent (audio) | ||
236 | 7 = V.110, 9600 baud | ||
237 | 8 = V.110, 19200 baud | ||
238 | 9 = V.110, 38400 baud | ||
239 | 10 = Analog Modem (only if hardware supports this) | ||
240 | 11 = Fax G3 (only if hardware supports this) | ||
241 | 15 0 Layer-3 protocol: | ||
242 | 0 = transparent | ||
243 | 1 = transparent with audio features (e.g. DSP) | ||
244 | 2 = Fax G3 Class 2 commands (S14 has to be set to 11) | ||
245 | 3 = Fax G3 Class 1 commands (S14 has to be set to 11) | ||
246 | 16 250 Send-Packet-size/16 | ||
247 | 17 8 Window-size (not yet implemented) | ||
248 | 18 4 Bit coded register, Service-Octet-1 to accept, | ||
249 | or to be used on dialout: | ||
250 | Bit 0: Service 1 (audio) when set. | ||
251 | Bit 1: Service 5 (BTX) when set. | ||
252 | Bit 2: Service 7 (data) when set. | ||
253 | Note: It is possible to set more than one | ||
254 | bit. In this case, on incoming calls | ||
255 | the selected services are accepted, | ||
256 | and if the service is "audio", the | ||
257 | Layer-2-protocol is automatically | ||
258 | changed to 4 regardless of the setting | ||
259 | of register 14. On outgoing calls, | ||
260 | the most significant 1-bit is chosen to | ||
261 | select the outgoing service octet. | ||
262 | 19 0 Service-Octet-2 | ||
263 | 20 0 Bit coded register (readonly) | ||
264 | Service-Octet-1 of last call. | ||
265 | Bit mapping is the same as register 18 | ||
266 | 21 0 Bit coded register (readonly) | ||
267 | Set on incoming call (during RING) to | ||
268 | octet 3 of calling party number IE (Numbering plan) | ||
269 | See section 4.5.10 of ITU Q.931 | ||
270 | 22 0 Bit coded register (readonly) | ||
271 | Set on incoming call (during RING) to | ||
272 | octet 3a of calling party number IE (Screening info) | ||
273 | See section 4.5.10 of ITU Q.931 | ||
274 | 23 0 Bit coded register: | ||
275 | Bit 0: 0 = Add CPN to RING message off | ||
276 | 1 = Add CPN to RING message on | ||
277 | Bit 1: 0 = Add CPN to FCON message off | ||
278 | 1 = Add CPN to FCON message on | ||
279 | Bit 2: 0 = Add CDN to RING/FCON message off | ||
280 | 1 = Add CDN to RING/FCON message on | ||
281 | |||
282 | Last but not least a (at the moment fairly primitive) device to request | ||
283 | the line-status (/dev/isdninfo) is made available. | ||
284 | |||
285 | Automatic assignment of devices to lines: | ||
286 | |||
287 | All inactive physical lines are listening to all EAZs for incoming | ||
288 | calls and are NOT assigned to a specific tty or network interface. | ||
289 | When an incoming call is detected, the driver looks first for a network | ||
290 | interface and then for an opened tty which: | ||
291 | |||
292 | 1. is configured for the same EAZ. | ||
293 | 2. has the same protocol settings for the B-channel. | ||
294 | 3. (only for network interfaces if the security flag is set) | ||
295 | contains the caller number in its access list. | ||
296 | 4. Either the channel is not bound exclusively to another Net-interface, or | ||
297 | it is bound AND the other checks apply to exactly this interface. | ||
298 | (For usage of the bind-features, refer to the isdnctrl-man-page) | ||
299 | |||
300 | Only when a matching interface or tty is found is the call accepted | ||
301 | and the "connection" between the low-level-layer and the link-level-layer | ||
302 | is established and kept until the end of the connection. | ||
303 | In all other cases no connection is established. Isdn4linux can be | ||
304 | configured to either do NOTHING in this case (which is useful, if | ||
305 | other, external devices with the same EAZ/MSN are connected to the bus) | ||
306 | or to reject the call actively. (isdnctrl busreject ...) | ||
307 | |||
308 | For an outgoing call, the inactive physical lines are searched. | ||
309 | The call is placed on the first physical line, which supports the | ||
310 | requested protocols for the B-channel. If a net-interface, however | ||
311 | is pre-bound to a channel, this channel is used directly. | ||
312 | |||
313 | This makes it possible to configure several network interfaces and ttys | ||
314 | for one EAZ, if the network interfaces are set to secure operation. | ||
315 | If an incoming call matches one network interface, it gets connected to it. | ||
316 | If another incoming call for the same EAZ arrives, which does not match | ||
317 | a network interface, the first tty gets a "RING" and so on. | ||
318 | |||
319 | 2 System prerequisites: | ||
320 | |||
321 | ATTENTION! | ||
322 | |||
323 | Always use the latest module utilities. The current version is | ||
324 | named in Documentation/Changes. Some old versions of insmod | ||
325 | are not capable of setting the driver-Ids correctly. | ||
326 | |||
327 | 3. Lowlevel-driver configuration. | ||
328 | |||
329 | Configuration depends on how the drivers are built. See the | ||
330 | README.<yourDriver> for information on driver-specific setup. | ||
331 | |||
332 | 4. Device-inodes | ||
333 | |||
334 | The major and minor numbers and their names are described in | ||
335 | Documentation/admin-guide/devices.rst. The major numbers are: | ||
336 | |||
337 | 43 for the ISDN-tty's. | ||
338 | 44 for the ISDN-callout-tty's. | ||
339 | 45 for control/info/debug devices. | ||
340 | |||
341 | 5. Application | ||
342 | |||
343 | a) For some card-types, firmware has to be loaded into the cards, before | ||
344 | proceeding with device-independent setup. See README.<yourDriver> | ||
345 | for how to do that. | ||
346 | |||
347 | b) If you only intend to use ttys, you are nearly ready now. | ||
348 | |||
349 | c) If you want to have really permanent "Modem"-settings on disk, you | ||
350 | can start the daemon iprofd. Give it a path to a file at the command- | ||
351 | line. It will store the profile-settings in this file every time | ||
352 | an AT&W0 is performed on any ISDN-tty. If the file already exists, | ||
353 | all profiles are initialized from this file. If you want to unload | ||
354 | any of the modules, kill iprofd first. | ||
355 | |||
356 | d) For networking, continue: Create an interface: | ||
357 | isdnctrl addif isdn0 | ||
358 | |||
359 | e) Set the EAZ (or MSN for Euro-ISDN): | ||
360 | isdnctrl eaz isdn0 2 | ||
361 | |||
362 | (For 1TR6 a single digit is allowed, for Euro-ISDN the number is your | ||
363 | real MSN e.g.: Phone-Number) | ||
364 | |||
365 | f) Set the number for outgoing calls on the interface: | ||
366 | isdnctrl addphone isdn0 out 1234567 | ||
367 | ... (this can be executed more than once, all assigned numbers are | ||
368 | tried in order) | ||
369 | and the number(s) for incoming calls: | ||
370 | isdnctrl addphone isdn0 in 1234567 | ||
371 | |||
372 | g) Set the timeout for hang-up: | ||
373 | isdnctrl huptimeout isdn0 <timeout_in_seconds> | ||
374 | |||
375 | h) additionally you may activate charge-hang-up (= Hang up before | ||
376 | next charge-info, this only works, if your isdn-provider transmits | ||
377 | the charge-info during and after the connection): | ||
378 | isdnctrl chargehup isdn0 on | ||
379 | |||
380 | i) Set the dial mode of the interface: | ||
381 | isdnctrl dialmode isdn0 auto | ||
382 | "off" means that you (or the system) cannot make any connection | ||
383 | (neither incoming or outgoing connections are possible). Use | ||
384 | this if you want to be sure that no connections will be made. | ||
385 | "auto" means that the interface is in auto-dial mode, and will | ||
386 | attempt to make a connection whenever a network data packet needs | ||
387 | the interface's link. Note that this can cause unexpected dialouts, | ||
388 | and lead to a high phone bill! Some daemons or other pc's that use | ||
389 | this interface can cause this. | ||
390 | Incoming connections are also possible. | ||
391 | "manual" is a dial mode created to prevent the unexpected dialouts. | ||
392 | In this mode, the interface will never make any connections on its | ||
393 | own. You must explicitly initiate a connection with "isdnctrl dial | ||
394 | isdn0". However, after an idle time of no traffic as configured for | ||
395 | the huptimeout value with isdnctrl, the connection _will_ be ended. | ||
396 | If you don't want any automatic hangup, set the huptimeout value to 0. | ||
397 | "manual" is the default. | ||
398 | |||
399 | j) Setup the interface with ifconfig as usual, and set a route to it. | ||
400 | |||
401 | k) (optional) If you run X11 and have Tcl/Tk-wish version 4.0, you can use | ||
402 | the script tools/tcltk/isdnmon. You can add actions for line-status | ||
403 | changes. See the comments at the beginning of the script for how to | ||
404 | do that. There are other tty-based tools in the tools-subdirectory | ||
405 | contributed by Michael Knigge (imon), Volker Götz (imontty) and | ||
406 | Andreas Kool (isdnmon). | ||
407 | |||
408 | l) For initial testing, you can set the verbose-level to 2 (default: 0). | ||
409 | Then all incoming calls are logged, even if they are not addressed | ||
410 | to one of the configured net-interfaces: | ||
411 | isdnctrl verbose 2 | ||
412 | |||
413 | Now you are ready! A ping to the set address should now result in an | ||
414 | automatic dial-out (look at syslog kernel-messages). | ||
415 | The phone numbers and EAZs can be assigned at any time with isdnctrl. | ||
416 | You can add as many interfaces as you like with addif following the | ||
417 | directions above. Of course, there may be some limitations. But we have | ||
418 | tested as many as 20 interfaces without any problem. However, if you | ||
419 | don't give an interface name to addif, the kernel will assign a name | ||
420 | which starts with "eth". The number of "eth"-interfaces is limited by | ||
421 | the kernel. | ||
422 | |||
423 | 5. Additional options for isdnctrl: | ||
424 | |||
425 | "isdnctrl secure <InterfaceName> on" | ||
426 | Only incoming calls, for which the caller-id is listed in the access | ||
427 | list of the interface are accepted. You can add caller-id's With the | ||
428 | command "isdnctrl addphone <InterfaceName> in <caller-id>" | ||
429 | Euro-ISDN does not transmit the leading '0' of the caller-id for an | ||
430 | incoming call, therefore you should configure it accordingly. | ||
431 | If the real number for the dialout e.g. is "09311234567" the number | ||
432 | to configure here is "9311234567". The pattern-match function | ||
433 | works similar to the shell mechanism. | ||
434 | |||
435 | ? one arbitrary digit | ||
436 | * zero or arbitrary many digits | ||
437 | [123] one of the digits in the list | ||
438 | [1-5] one digit between '1' and '5' | ||
439 | a '^' as the first character in a list inverts the list | ||
440 | |||
441 | |||
442 | "isdnctrl secure <InterfaceName> off" | ||
443 | Switch off secure operation (default). | ||
444 | |||
445 | "isdnctrl ihup <InterfaceName> [on|off]" | ||
446 | Switch the hang-up-timer for incoming calls on or off. | ||
447 | |||
448 | "isdnctrl eaz <InterfaceName>" | ||
449 | Returns the EAZ of an interface. | ||
450 | |||
451 | "isdnctrl delphone <InterfaceName> in|out <number>" | ||
452 | Deletes a number from one of the access-lists of the interface. | ||
453 | |||
454 | "isdnctrl delif <InterfaceName>" | ||
455 | Removes the interface (and possible slaves) from the kernel. | ||
456 | (You have to unregister it with "ifconfig <InterfaceName> down" before). | ||
457 | |||
458 | "isdnctrl callback <InterfaceName> [on|off]" | ||
459 | Switches an interface to callback-mode. In this mode, an incoming call | ||
460 | will be rejected and after this the remote-station will be called. If | ||
461 | you test this feature by using ping, some routers will re-dial very | ||
462 | quickly, so that the callback from isdn4linux may not be recognized. | ||
463 | In this case use ping with the option -i <sec> to increase the interval | ||
464 | between echo-packets. | ||
465 | |||
466 | "isdnctrl cbdelay <InterfaceName> [seconds]" | ||
467 | Sets the delay (default 5 sec) between an incoming call and start of | ||
468 | dialing when callback is enabled. | ||
469 | |||
470 | "isdnctrl cbhup <InterfaceName> [on|off]" | ||
471 | This enables (default) or disables an active hangup (reject) when getting an | ||
472 | incoming call for an interface which is configured for callback. | ||
473 | |||
474 | "isdnctrl encap <InterfaceName> <EncapType>" | ||
475 | Selects the type of packet-encapsulation. The encapsulation can be changed | ||
476 | only while an interface is down. | ||
477 | |||
478 | At the moment the following values are supported: | ||
479 | |||
480 | rawip (Default) Selects raw-IP-encapsulation. This means, MAC-headers | ||
481 | are stripped off. | ||
482 | ip IP with type-field. Same as IP but the type-field of the MAC-header | ||
483 | is preserved. | ||
484 | x25iface X.25 interface encapsulation (first byte semantics as defined in | ||
485 | ../networking/x25-iface.txt). Use this for running the linux | ||
486 | X.25 network protocol stack (AF_X25 sockets) on top of isdn. | ||
487 | cisco-h A special-mode for communicating with a Cisco, which is configured | ||
488 | to do "hdlc" | ||
489 | ethernet No stripping. Packets are sent with full MAC-header. | ||
490 | The Ethernet-address of the interface is faked, from its | ||
491 | IP-address: fc:fc:i1:i2:i3:i4, where i1-4 are the IP-addr.-values. | ||
492 | syncppp Synchronous PPP | ||
493 | |||
494 | uihdlc HDLC with UI-frame-header (for use with DOS ISPA, option -h1) | ||
495 | |||
496 | |||
497 | NOTE: x25iface encapsulation is currently experimental. Please | ||
498 | read README.x25 for further details | ||
499 | |||
500 | |||
501 | Watching packets, using standard-tcpdump will fail for all encapsulations | ||
502 | except ethernet because tcpdump does not know how to handle packets | ||
503 | without MAC-header. A patch for tcpdump is included in the utility-package | ||
504 | mentioned above. | ||
505 | |||
506 | "isdnctrl l2_prot <InterfaceName> <L2-ProtocolName>" | ||
507 | Selects a layer-2-protocol. | ||
508 | (With the ICN-driver and the HiSax-driver, "x75i" and "hdlc" is available. | ||
509 | With other drivers, "x75ui", "x75bui", "x25dte", "x25dce" may be | ||
510 | possible too. See README.x25 for x25 related l2 protocols.) | ||
511 | |||
512 | isdnctrl l3_prot <InterfaceName> <L3-ProtocolName> | ||
513 | The same for layer-3. (At the moment only "trans" is allowed) | ||
514 | |||
515 | "isdnctrl list <InterfaceName>" | ||
516 | Shows all parameters of an interface and the charge-info. | ||
517 | Try "all" as the interface name. | ||
518 | |||
519 | "isdnctrl hangup <InterfaceName>" | ||
520 | Forces hangup of an interface. | ||
521 | |||
522 | "isdnctrl bind <InterfaceName> <DriverId>,<ChannelNumber> [exclusive]" | ||
523 | If you are using more than one ISDN card, it is sometimes necessary to | ||
524 | dial out using a specific card or even preserve a specific channel for | ||
525 | dialout of a specific net-interface. This can be done with the above | ||
526 | command. Replace <DriverId> by whatever you assigned while loading the | ||
527 | module. The <ChannelNumber> is counted from zero. The upper limit | ||
528 | depends on the card used. At the moment no card supports more than | ||
529 | 2 channels, so the upper limit is one. | ||
530 | |||
531 | "isdnctrl unbind <InterfaceName>" | ||
532 | unbinds a previously bound interface. | ||
533 | |||
534 | "isdnctrl busreject <DriverId> on|off" | ||
535 | If switched on, isdn4linux replies a REJECT to incoming calls, it | ||
536 | cannot match to any configured interface. | ||
537 | If switched off, nothing happens in this case. | ||
538 | You normally should NOT enable this feature, if the ISDN adapter is not | ||
539 | the only device connected to the S0-bus. Otherwise it could happen that | ||
540 | isdn4linux rejects an incoming call, which belongs to another device on | ||
541 | the bus. | ||
542 | |||
543 | "isdnctrl addslave <InterfaceName> <SlaveName> | ||
544 | Creates a slave interface for channel-bundling. Slave interfaces are | ||
545 | not seen by the kernel, but their ISDN-part can be configured with | ||
546 | isdnctrl as usual. (Phone numbers, EAZ/MSN, timeouts etc.) If more | ||
547 | than two channels are to be bundled, feel free to create as many as you | ||
548 | want. InterfaceName must be a real interface, NOT a slave. Slave interfaces | ||
549 | start dialing, if the master interface resp. the previous slave interface | ||
550 | has a load of more than 7000 cps. They hangup if the load goes under 7000 | ||
551 | cps, according to their "huptimeout"-parameter. | ||
552 | |||
553 | "isdnctrl sdelay <InterfaceName> secs." | ||
554 | This sets the minimum time an Interface has to be fully loaded, until | ||
555 | it sends a dial-request to its slave. | ||
556 | |||
557 | "isdnctrl dial <InterfaceName>" | ||
558 | Forces an interface to start dialing even if no packets are to be | ||
559 | transferred. | ||
560 | |||
561 | "isdnctrl mapping <DriverId> MSN0,MSN1,MSN2,...MSN9" | ||
562 | This installs a mapping table for EAZ<->MSN-mapping for a single line. | ||
563 | Missing MSN's have to be given as "-" or can be omitted, if at the end | ||
564 | of the commandline. | ||
565 | With this command, it's now possible to have an interface listening to | ||
566 | mixed 1TR6- and Euro-Type lines. In this case, the interface has to be | ||
567 | configured to a 1TR6-type EAZ (one digit). The mapping is also valid | ||
568 | for tty-emulation. Seen from the interface/tty-level the mapping | ||
569 | CAN be used, however it's possible to use single tty's/interfaces with | ||
570 | real MSN's (more digits) also, in which case the mapping will be ignored. | ||
571 | Here is an example: | ||
572 | |||
573 | You have a 1TR6-type line with base-nr. 1234567 and a Euro-line with | ||
574 | MSN's 987654, 987655 and 987656. The DriverId for the Euro-line is "EURO". | ||
575 | |||
576 | isdnctrl mapping EURO -,987654,987655,987656,-,987655 | ||
577 | ... | ||
578 | isdnctrl eaz isdn0 1 # listen on 12345671(1tr6) and 987654(euro) | ||
579 | ... | ||
580 | isdnctrl eaz isdn1 4 # listen on 12345674(1tr6) only. | ||
581 | ... | ||
582 | isdnctrl eaz isdn2 987654 # listen on 987654(euro) only. | ||
583 | |||
584 | Same scheme is used with AT&E... at the tty's. | ||
585 | |||
586 | 6. If you want to write a new low-level-driver, you are welcome. | ||
587 | The interface to the link-level-module is described in the file INTERFACE. | ||
588 | If the interface should be expanded for any reason, don't do it | ||
589 | on your own, send me a mail containing the proposed changes and | ||
590 | some reasoning about them. | ||
591 | If other drivers will not be affected, I will include the changes | ||
592 | in the next release. | ||
593 | For developers only, there is a second mailing-list. Write to me | ||
594 | (fritz@isdn4linux.de), if you want to join that list. | ||
595 | |||
596 | Have fun! | ||
597 | |||
598 | -Fritz | ||
599 | |||
diff --git a/Documentation/isdn/README.FAQ b/Documentation/isdn/README.FAQ deleted file mode 100644 index e5dd1addacdd..000000000000 --- a/Documentation/isdn/README.FAQ +++ /dev/null | |||
@@ -1,26 +0,0 @@ | |||
1 | |||
2 | The FAQ for isdn4linux | ||
3 | ====================== | ||
4 | |||
5 | Please note that there is a big FAQ available in the isdn4k-utils. | ||
6 | You find it in: | ||
7 | isdn4k-utils/FAQ/i4lfaq.sgml | ||
8 | |||
9 | In case you just want to see the FAQ online, or download the newest version, | ||
10 | you can have a look at my website: | ||
11 | https://www.mhessler.de/i4lfaq/ (view + download) | ||
12 | or: | ||
13 | https://www.isdn4linux.de/faq/4lfaq.html (view) | ||
14 | |||
15 | As the extension tells, the FAQ is in SGML format, and you can convert it | ||
16 | into text/html/... format by using the sgml2txt/sgml2html/... tools. | ||
17 | Alternatively, you can also do a 'configure; make all' in the FAQ directory. | ||
18 | |||
19 | |||
20 | Please have a look at the FAQ before posting anything in the Mailinglist, | ||
21 | or the newsgroup! | ||
22 | |||
23 | |||
24 | Matthias Hessler | ||
25 | hessler@isdn4linux.de | ||
26 | |||
diff --git a/Documentation/isdn/README.audio b/Documentation/isdn/README.audio deleted file mode 100644 index 8ebca19290d9..000000000000 --- a/Documentation/isdn/README.audio +++ /dev/null | |||
@@ -1,138 +0,0 @@ | |||
1 | $Id: README.audio,v 1.8 1999/07/11 17:17:29 armin Exp $ | ||
2 | |||
3 | ISDN subsystem for Linux. | ||
4 | Description of audio mode. | ||
5 | |||
6 | When enabled during kernel configuration, the tty emulator of the ISDN | ||
7 | subsystem is capable of a reduced set of commands to support audio. | ||
8 | This document describes the commands supported and the format of | ||
9 | audio data. | ||
10 | |||
11 | Commands for enabling/disabling audio mode: | ||
12 | |||
13 | AT+FCLASS=8 Enable audio mode. | ||
14 | This affects the following registers: | ||
15 | S18: Bits 0 and 2 are set. | ||
16 | S16: Set to 48 and any further change to | ||
17 | larger values is blocked. | ||
18 | AT+FCLASS=0 Disable audio mode. | ||
19 | Register 18 is set to 4. | ||
20 | AT+FCLASS=? Show possible modes. | ||
21 | AT+FCLASS? Report current mode (0 or 8). | ||
22 | |||
23 | Commands supported in audio mode: | ||
24 | |||
25 | All audio mode commands have one of the following forms: | ||
26 | |||
27 | AT+Vxx? Show current setting. | ||
28 | AT+Vxx=? Show possible settings. | ||
29 | AT+Vxx=v Set simple parameter. | ||
30 | AT+Vxx=v,v ... Set complex parameter. | ||
31 | |||
32 | where xx is a two-character code and v are alphanumerical parameters. | ||
33 | The following commands are supported: | ||
34 | |||
35 | AT+VNH=x Auto hangup setting. NO EFFECT, supported | ||
36 | for compatibility only. | ||
37 | AT+VNH? Always reporting "1" | ||
38 | AT+VNH=? Always reporting "1" | ||
39 | |||
40 | AT+VIP Reset all audio parameters. | ||
41 | |||
42 | AT+VLS=x Line select. x is one of the following: | ||
43 | 0 = No device. | ||
44 | 2 = Phone line. | ||
45 | AT+VLS=? Always reporting "0,2" | ||
46 | AT+VLS? Show current line. | ||
47 | |||
48 | AT+VRX Start recording. Emulator responds with | ||
49 | CONNECT and starts sending audio data to | ||
50 | the application. See below for data format | ||
51 | |||
52 | AT+VSD=x,y Set silence-detection parameters. | ||
53 | Possible parameters: | ||
54 | x = 0 ... 31 sensitivity threshold level. | ||
55 | (default 0 , deactivated) | ||
56 | y = 0 ... 255 range of interval in units | ||
57 | of 0.1 second. (default 70) | ||
58 | AT+VSD=? Report possible parameters. | ||
59 | AT+VSD? Show current parameters. | ||
60 | |||
61 | AT+VDD=x,y Set DTMF-detection parameters. | ||
62 | Only possible if online and during this connection. | ||
63 | Possible parameters: | ||
64 | x = 0 ... 15 sensitivity threshold level. | ||
65 | (default 0 , I4L soft-decode) | ||
66 | (1-15 soft-decode off, hardware on) | ||
67 | y = 0 ... 255 tone duration in units of 5ms. | ||
68 | Not for I4L soft decode (default 8, 40ms) | ||
69 | AT+VDD=? Report possible parameters. | ||
70 | AT+VDD? Show current parameters. | ||
71 | |||
72 | AT+VSM=x Select audio data format. | ||
73 | Possible parameters: | ||
74 | 2 = ADPCM-2 | ||
75 | 3 = ADPCM-3 | ||
76 | 4 = ADPCM-4 | ||
77 | 5 = aLAW | ||
78 | 6 = uLAW | ||
79 | AT+VSM=? Show possible audio formats. | ||
80 | |||
81 | AT+VTX Start audio playback. Emulator responds | ||
82 | with CONNECT and starts sending audio data | ||
83 | received from the application via phone line. | ||
84 | General behavior and description of data formats/protocol. | ||
85 | when a connection is made: | ||
86 | |||
87 | On incoming calls, if the application responds to a RING | ||
88 | with ATA, depending on the calling service, the emulator | ||
89 | responds with either CONNECT (data call) or VCON (voice call). | ||
90 | |||
91 | On outgoing voice calls, the emulator responds with VCON | ||
92 | upon connection setup. | ||
93 | |||
94 | Audio recording. | ||
95 | |||
96 | When receiving audio data, a kind of bisync protocol is used. | ||
97 | Upon AT+VRX command, the emulator responds with CONNECT, and | ||
98 | starts sending audio data to the application. There are several | ||
99 | escape sequences defined, all using DLE (0x10) as Escape char: | ||
100 | |||
101 | <DLE><ETX> End of audio data. (i.e. caused by a | ||
102 | hangup of the remote side) Emulator stops | ||
103 | recording, responding with VCON. | ||
104 | <DLE><DC4> Abort recording, (send by appl.) Emulator | ||
105 | stops recording, sends DLE,ETX. | ||
106 | <DLE><DLE> Escape sequence for DLE in data stream. | ||
107 | <DLE>0 Touchtone "0" received. | ||
108 | ... | ||
109 | <DLE>9 Touchtone "9" received. | ||
110 | <DLE># Touchtone "#" received. | ||
111 | <DLE>* Touchtone "*" received. | ||
112 | <DLE>A Touchtone "A" received. | ||
113 | <DLE>B Touchtone "B" received. | ||
114 | <DLE>C Touchtone "C" received. | ||
115 | <DLE>D Touchtone "D" received. | ||
116 | |||
117 | <DLE>q quiet. Silence detected after non-silence. | ||
118 | <DLE>s silence. Silence detected from the | ||
119 | start of recording. | ||
120 | |||
121 | Currently unsupported DLE sequences: | ||
122 | |||
123 | <DLE>c FAX calling tone received. | ||
124 | <DLE>b busy tone received. | ||
125 | |||
126 | Audio playback. | ||
127 | |||
128 | When sending audio data, upon AT+VTX command, emulator responds with | ||
129 | CONNECT, and starts transferring data from application to the phone line. | ||
130 | The same DLE sequences apply to this mode. | ||
131 | |||
132 | Full-Duplex-Audio: | ||
133 | |||
134 | When _both_ commands for recording and playback are given in _one_ | ||
135 | AT-command-line (i.e.: "AT+VTX+VRX"), full-duplex-mode is selected. | ||
136 | In this mode, the only way to stop recording is sending <DLE><DC4> | ||
137 | and the only way to stop playback is to send <DLE><ETX>. | ||
138 | |||
diff --git a/Documentation/isdn/README.concap b/Documentation/isdn/README.concap deleted file mode 100644 index a76d74845a4c..000000000000 --- a/Documentation/isdn/README.concap +++ /dev/null | |||
@@ -1,259 +0,0 @@ | |||
1 | Description of the "concap" encapsulation protocol interface | ||
2 | ============================================================ | ||
3 | |||
4 | The "concap" interface is intended to be used by network device | ||
5 | drivers that need to process an encapsulation protocol. | ||
6 | It is assumed that the protocol interacts with a linux network device by | ||
7 | - data transmission | ||
8 | - connection control (establish, release) | ||
9 | Thus, the mnemonic: "CONnection CONtrolling eNCAPsulation Protocol". | ||
10 | |||
11 | This is currently only used inside the isdn subsystem. But it might | ||
12 | also be useful to other kinds of network devices. Thus, if you want | ||
13 | to suggest changes that improve usability or performance of the | ||
14 | interface, please let me know. I'm willing to include them in future | ||
15 | releases (even if I needed to adapt the current isdn code to the | ||
16 | changed interface). | ||
17 | |||
18 | |||
19 | Why is this useful? | ||
20 | =================== | ||
21 | |||
22 | The encapsulation protocol used on top of WAN connections or permanent | ||
23 | point-to-point links are frequently chosen upon bilateral agreement. | ||
24 | Thus, a device driver for a certain type of hardware must support | ||
25 | several different encapsulation protocols at once. | ||
26 | |||
27 | The isdn device driver did already support several different | ||
28 | encapsulation protocols. The encapsulation protocol is configured by a | ||
29 | user space utility (isdnctrl). The isdn network interface code then | ||
30 | uses several case statements which select appropriate actions | ||
31 | depending on the currently configured encapsulation protocol. | ||
32 | |||
33 | In contrast, LAN network interfaces always used a single encapsulation | ||
34 | protocol which is unique to the hardware type of the interface. The LAN | ||
35 | encapsulation is usually done by just sticking a header on the data. Thus, | ||
36 | traditional linux network device drivers used to process the | ||
37 | encapsulation protocol directly (usually by just providing a hard_header() | ||
38 | method in the device structure) using some hardware type specific support | ||
39 | functions. This is simple, direct and efficient. But it doesn't fit all | ||
40 | the requirements for complex WAN encapsulations. | ||
41 | |||
42 | |||
43 | The configurability of the encapsulation protocol to be used | ||
44 | makes isdn network interfaces more flexible, but also much more | ||
45 | complex than traditional lan network interfaces. | ||
46 | |||
47 | |||
48 | Many Encapsulation protocols used on top of WAN connections will not just | ||
49 | stick a header on the data. They also might need to set up or release | ||
50 | the WAN connection. They also might want to send other data for their | ||
51 | private purpose over the wire, e.g. ppp does a lot of link level | ||
52 | negotiation before the first piece of user data can be transmitted. | ||
53 | Such encapsulation protocols for WAN devices are typically more complex | ||
54 | than encapsulation protocols for lan devices. Thus, network interface | ||
55 | code for typical WAN devices also tends to be more complex. | ||
56 | |||
57 | |||
58 | In order to support Linux' x25 PLP implementation on top of | ||
59 | isdn network interfaces I could have introduced yet another branch to | ||
60 | the various case statements inside drivers/isdn/isdn_net.c. | ||
61 | This eventually made isdn_net.c even more complex. In addition, it made | ||
62 | isdn_net.c harder to maintain. Thus, by identifying an abstract | ||
63 | interface between the network interface code and the encapsulation | ||
64 | protocol, complexity could be reduced and maintainability could be | ||
65 | increased. | ||
66 | |||
67 | |||
68 | Likewise, a similar encapsulation protocol will frequently be needed by | ||
69 | several different interfaces of even different hardware type, e.g. the | ||
70 | synchronous ppp implementation used by the isdn driver and the | ||
71 | asynchronous ppp implementation used by the ppp driver have a lot of | ||
72 | similar code in them. By cleanly separating the encapsulation protocol | ||
73 | from the hardware specific interface stuff such code could be shared | ||
74 | better in future. | ||
75 | |||
76 | |||
77 | When operating over dial-up-connections (e.g. telephone lines via modem, | ||
78 | non-permanent virtual circuits of wide area networks, ISDN) many | ||
79 | encapsulation protocols will need to control the connection. Therefore, | ||
80 | some basic connection control primitives are supported. The type and | ||
81 | semantics of the connection (i.e the ISO layer where connection service | ||
82 | is provided) is outside our scope and might be different depending on | ||
83 | the encapsulation protocol used, e.g. for a ppp module using our service | ||
84 | on top of a modem connection a connect_request will result in dialing | ||
85 | a (somewhere else configured) remote phone number. For an X25-interface | ||
86 | module (LAPB semantics, as defined in Documentation/networking/x25-iface.txt) | ||
87 | a connect_request will ask for establishing a reliable lapb | ||
88 | datalink connection. | ||
89 | |||
90 | |||
91 | The encapsulation protocol currently provides the following | ||
92 | service primitives to the network device. | ||
93 | |||
94 | - create a new encapsulation protocol instance | ||
95 | - delete encapsulation protocol instance and free all its resources | ||
96 | - initialize (open) the encapsulation protocol instance for use. | ||
97 | - deactivate (close) an encapsulation protocol instance. | ||
98 | - process (xmit) data handed down by upper protocol layer | ||
99 | - receive data from lower (hardware) layer | ||
100 | - process connect indication from lower (hardware) layer | ||
101 | - process disconnect indication from lower (hardware) layer | ||
102 | |||
103 | |||
104 | The network interface driver accesses those primitives via callbacks | ||
105 | provided by the encapsulation protocol instance within a | ||
106 | struct concap_proto_ops. | ||
107 | |||
108 | struct concap_proto_ops{ | ||
109 | |||
110 | /* create a new encapsulation protocol instance of same type */ | ||
111 | struct concap_proto * (*proto_new) (void); | ||
112 | |||
113 | /* delete encapsulation protocol instance and free all its resources. | ||
114 | cprot may no longer be referenced after calling this */ | ||
115 | void (*proto_del)(struct concap_proto *cprot); | ||
116 | |||
117 | /* initialize the protocol's data. To be called at interface startup | ||
118 | or when the device driver resets the interface. All services of the | ||
119 | encapsulation protocol may be used after this*/ | ||
120 | int (*restart)(struct concap_proto *cprot, | ||
121 | struct net_device *ndev, | ||
122 | struct concap_device_ops *dops); | ||
123 | |||
124 | /* deactivate an encapsulation protocol instance. The encapsulation | ||
125 | protocol may not call any *dops methods after this. */ | ||
126 | int (*close)(struct concap_proto *cprot); | ||
127 | |||
128 | /* process a frame handed down to us by upper layer */ | ||
129 | int (*encap_and_xmit)(struct concap_proto *cprot, struct sk_buff *skb); | ||
130 | |||
131 | /* to be called for each data entity received from lower layer*/ | ||
132 | int (*data_ind)(struct concap_proto *cprot, struct sk_buff *skb); | ||
133 | |||
134 | /* to be called when a connection was set up/down. | ||
135 | Protocols that don't process these primitives might fill in | ||
136 | dummy methods here */ | ||
137 | int (*connect_ind)(struct concap_proto *cprot); | ||
138 | int (*disconn_ind)(struct concap_proto *cprot); | ||
139 | }; | ||
140 | |||
141 | |||
142 | The data structures are defined in the header file include/linux/concap.h. | ||
143 | |||
144 | |||
145 | A Network interface using encapsulation protocols must also provide | ||
146 | some service primitives to the encapsulation protocol: | ||
147 | |||
148 | - request data being submitted by lower layer (device hardware) | ||
149 | - request a connection being set up by lower layer | ||
150 | - request a connection being released by lower layer | ||
151 | |||
152 | The encapsulation protocol accesses those primitives via callbacks | ||
153 | provided by the network interface within a struct concap_device_ops. | ||
154 | |||
155 | struct concap_device_ops{ | ||
156 | |||
157 | /* to request data be submitted by device */ | ||
158 | int (*data_req)(struct concap_proto *, struct sk_buff *); | ||
159 | |||
160 | /* Control methods must be set to NULL by devices which do not | ||
161 | support connection control. */ | ||
162 | /* to request a connection be set up */ | ||
163 | int (*connect_req)(struct concap_proto *); | ||
164 | |||
165 | /* to request a connection be released */ | ||
166 | int (*disconn_req)(struct concap_proto *); | ||
167 | }; | ||
168 | |||
169 | The network interface does not explicitly provide a receive service | ||
170 | because the encapsulation protocol directly calls netif_rx(). | ||
171 | |||
172 | |||
173 | |||
174 | |||
175 | An encapsulation protocol itself is actually the | ||
176 | struct concap_proto{ | ||
177 | struct net_device *net_dev; /* net device using our service */ | ||
178 | struct concap_device_ops *dops; /* callbacks provided by device */ | ||
179 | struct concap_proto_ops *pops; /* callbacks provided by us */ | ||
180 | int flags; | ||
181 | void *proto_data; /* protocol specific private data, to | ||
182 | be accessed via *pops methods only*/ | ||
183 | /* | ||
184 | : | ||
185 | whatever | ||
186 | : | ||
187 | */ | ||
188 | }; | ||
189 | |||
190 | Most of this is filled in when the device requests the protocol to | ||
191 | be reset (opend). The network interface must provide the net_dev and | ||
192 | dops pointers. Other concap_proto members should be considered private | ||
193 | data that are only accessed by the pops callback functions. Likewise, | ||
194 | a concap proto should access the network device's private data | ||
195 | only by means of the callbacks referred to by the dops pointer. | ||
196 | |||
197 | |||
198 | A possible extended device structure which uses the connection controlling | ||
199 | encapsulation services could look like this: | ||
200 | |||
201 | struct concap_device{ | ||
202 | struct net_device net_dev; | ||
203 | struct my_priv /* device->local stuff */ | ||
204 | /* the my_priv struct might contain a | ||
205 | struct concap_device_ops *dops; | ||
206 | to provide the device specific callbacks | ||
207 | */ | ||
208 | struct concap_proto *cprot; /* callbacks provided by protocol */ | ||
209 | }; | ||
210 | |||
211 | |||
212 | |||
213 | Misc Thoughts | ||
214 | ============= | ||
215 | |||
216 | The concept of the concap proto might help to reuse protocol code and | ||
217 | reduce the complexity of certain network interface implementations. | ||
218 | The trade off is that it introduces yet another procedure call layer | ||
219 | when processing the protocol. This has of course some impact on | ||
220 | performance. However, typically the concap interface will be used by | ||
221 | devices attached to slow lines (like telephone, isdn, leased synchronous | ||
222 | lines). For such slow lines, the overhead is probably negligible. | ||
223 | This might no longer hold for certain high speed WAN links (like | ||
224 | ATM). | ||
225 | |||
226 | |||
227 | If general linux network interfaces explicitly supported concap | ||
228 | protocols (e.g. by a member struct concap_proto* in struct net_device) | ||
229 | then the interface of the service function could be changed | ||
230 | by passing a pointer of type (struct net_device*) instead of | ||
231 | type (struct concap_proto*). Doing so would make many of the service | ||
232 | functions compatible to network device support functions. | ||
233 | |||
234 | e.g. instead of the concap protocol's service function | ||
235 | |||
236 | int (*encap_and_xmit)(struct concap_proto *cprot, struct sk_buff *skb); | ||
237 | |||
238 | we could have | ||
239 | |||
240 | int (*encap_and_xmit)(struct net_device *ndev, struct sk_buff *skb); | ||
241 | |||
242 | As this is compatible to the dev->hard_start_xmit() method, the device | ||
243 | driver could directly register the concap protocol's encap_and_xmit() | ||
244 | function as its hard_start_xmit() method. This would eliminate one | ||
245 | procedure call layer. | ||
246 | |||
247 | |||
248 | The device's data request function could also be defined as | ||
249 | |||
250 | int (*data_req)(struct net_device *ndev, struct sk_buff *skb); | ||
251 | |||
252 | This might even allow for some protocol stacking. And the network | ||
253 | interface might even register the same data_req() function directly | ||
254 | as its hard_start_xmit() method when a zero layer encapsulation | ||
255 | protocol is configured. Thus, eliminating the performance penalty | ||
256 | of the concap interface when a trivial concap protocol is used. | ||
257 | Nevertheless, the device remains able to support encapsulation | ||
258 | protocol configuration. | ||
259 | |||
diff --git a/Documentation/isdn/README.diversion b/Documentation/isdn/README.diversion deleted file mode 100644 index bddcd5fb86ff..000000000000 --- a/Documentation/isdn/README.diversion +++ /dev/null | |||
@@ -1,127 +0,0 @@ | |||
1 | The isdn diversion services are a supporting module working together with | ||
2 | the isdn4linux and the HiSax module for passive cards. | ||
3 | Active cards, TAs and cards using a own or other driver than the HiSax | ||
4 | module need to be adapted to the HL<->LL interface described in a separate | ||
5 | document. The diversion services may be used with all cards supported by | ||
6 | the HiSax driver. | ||
7 | The diversion kernel interface and controlling tool divertctrl were written | ||
8 | by Werner Cornelius (werner@isdn4linux.de or werner@titro.de) under the | ||
9 | GNU General Public License. | ||
10 | |||
11 | This program is free software; you can redistribute it and/or modify | ||
12 | it under the terms of the GNU General Public License as published by | ||
13 | the Free Software Foundation; either version 2 of the License, or | ||
14 | (at your option) any later version. | ||
15 | |||
16 | This program is distributed in the hope that it will be useful, | ||
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | GNU General Public License for more details. | ||
20 | |||
21 | You should have received a copy of the GNU General Public License | ||
22 | along with this program; if not, write to the Free Software | ||
23 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | |||
25 | Table of contents | ||
26 | ================= | ||
27 | |||
28 | 1. Features of the i4l diversion services | ||
29 | (Or what can the i4l diversion services do for me) | ||
30 | |||
31 | 2. Required hard- and software | ||
32 | |||
33 | 3. Compiling, installing and loading/unloading the module | ||
34 | Tracing calling and diversion information | ||
35 | |||
36 | 4. Tracing calling and diversion information | ||
37 | |||
38 | 5. Format of the divert device ASCII output | ||
39 | |||
40 | |||
41 | 1. Features of the i4l diversion services | ||
42 | (Or what can the i4l diversion services do for me) | ||
43 | |||
44 | The i4l diversion services offers call forwarding and logging normally | ||
45 | only supported by isdn phones. Incoming calls may be diverted | ||
46 | unconditionally (CFU), when not reachable (CFNR) or on busy condition | ||
47 | (CFB). | ||
48 | The diversions may be invoked statically in the providers exchange | ||
49 | as normally done by isdn phones. In this case all incoming calls | ||
50 | with a special (or all) service identifiers are forwarded if the | ||
51 | forwarding reason is met. Activated static services may also be | ||
52 | interrogated (queried). | ||
53 | The i4l diversion services additionally offers a dynamic version of | ||
54 | call forwarding which is not preprogrammed inside the providers exchange | ||
55 | but dynamically activated by i4l. | ||
56 | In this case all incoming calls are checked by rules that may be | ||
57 | compared to the mechanism of ipfwadm or ipchains. If a given rule matches | ||
58 | the checking process is finished and the rule matching will be applied | ||
59 | to the call. | ||
60 | The rules include primary and secondary service identifiers, called | ||
61 | number and subaddress, callers number and subaddress and whether the rule | ||
62 | matches to all filtered calls or only those when all B-channel resources | ||
63 | are exhausted. | ||
64 | Actions that may be invoked by a rule are ignore, proceed, reject, | ||
65 | direct divert or delayed divert of a call. | ||
66 | All incoming calls matching a rule except the ignore rule a reported and | ||
67 | logged as ASCII via the proc filesystem (/proc/net/isdn/divert). If proceed | ||
68 | is selected the call will be held in a proceeding state (without ringing) | ||
69 | for a certain amount of time to let an external program or client decide | ||
70 | how to handle the call. | ||
71 | |||
72 | |||
73 | 2. Required hard- and software | ||
74 | |||
75 | For using the i4l diversion services the isdn line must be of a EURO/DSS1 | ||
76 | type. Additionally the i4l services only work together with the HiSax | ||
77 | driver for passive isdn cards. All HiSax supported cards may be used for | ||
78 | the diversion purposes. | ||
79 | The static diversion services require the provider having static services | ||
80 | CFU, CFNR, CFB activated on an MSN-line. The static services may not be | ||
81 | used on a point-to-point connection. Further the static services are only | ||
82 | available in some countries (for example germany). Countries requiring the | ||
83 | keypad protocol for activating static diversions (like the netherlands) are | ||
84 | not supported but may use the tty devices for this purpose. | ||
85 | The dynamic diversion services may be used in all countries if the provider | ||
86 | enables the feature CF (call forwarding). This should work on both MSN- and | ||
87 | point-to-point lines. | ||
88 | To add and delete rules the additional divertctrl program is needed. This | ||
89 | program is part of the isdn4kutils package. | ||
90 | |||
91 | 3. Compiling, installing and loading/unloading the module | ||
92 | Tracing calling and diversion information | ||
93 | |||
94 | |||
95 | To compile the i4l code with diversion support you need to say yes to the | ||
96 | DSS1 diversion services when selecting the i4l options in the kernel | ||
97 | config (menuconfig or config). | ||
98 | After having properly activated a make modules and make modules_install all | ||
99 | required modules will be correctly installed in the needed modules dirs. | ||
100 | As the diversion services are currently not included in the scripts of most | ||
101 | standard distributions you will have to add a "insmod dss1_divert" after | ||
102 | having loaded the global isdn module. | ||
103 | The module can be loaded without any command line parameters. | ||
104 | If the module is actually loaded and active may be checked with a | ||
105 | "cat /proc/modules" or "ls /proc/net/isdn/divert". The divert file is | ||
106 | dynamically created by the diversion module and removed when the module is | ||
107 | unloaded. | ||
108 | |||
109 | |||
110 | 4. Tracing calling and diversion information | ||
111 | |||
112 | You also may put a "cat /proc/net/isdn/divert" in the background with the | ||
113 | output redirected to a file. Then all actions of the module are logged. | ||
114 | The divert file in the proc system may be opened more than once, so in | ||
115 | conjunction with inetd and a small remote client on other machines inside | ||
116 | your network incoming calls and reactions by the module may be shown on | ||
117 | every listening machine. | ||
118 | If a call is reported as proceeding an external program or client may | ||
119 | specify during a certain amount of time (normally 4 to 10 seconds) what | ||
120 | to do with that call. | ||
121 | To unload the module all open files to the device in the proc system must | ||
122 | be closed. Otherwise the module (and isdn.o) may not be unloaded. | ||
123 | |||
124 | 5. Format of the divert device ASCII output | ||
125 | |||
126 | To be done later | ||
127 | |||
diff --git a/Documentation/isdn/README.fax b/Documentation/isdn/README.fax deleted file mode 100644 index 5314958a8a6e..000000000000 --- a/Documentation/isdn/README.fax +++ /dev/null | |||
@@ -1,45 +0,0 @@ | |||
1 | |||
2 | Fax with isdn4linux | ||
3 | =================== | ||
4 | |||
5 | When enabled during kernel configuration, the tty emulator | ||
6 | of the ISDN subsystem is capable of the Fax Class 2 commands. | ||
7 | |||
8 | This only makes sense under the following conditions : | ||
9 | |||
10 | - You need the commands as dummy, because you are using | ||
11 | hylafax (with patch) for AVM capi. | ||
12 | - You want to use the fax capabilities of your isdn-card. | ||
13 | (supported cards are listed below) | ||
14 | |||
15 | |||
16 | NOTE: This implementation does *not* support fax with passive | ||
17 | ISDN-cards (known as softfax). The low-level driver of | ||
18 | the ISDN-card and/or the card itself must support this. | ||
19 | |||
20 | |||
21 | Supported ISDN-Cards | ||
22 | -------------------- | ||
23 | |||
24 | Eicon DIVA Server BRI/PCI | ||
25 | - full support with both B-channels. | ||
26 | |||
27 | Eicon DIVA Server 4BRI/PCI | ||
28 | - full support with all B-channels. | ||
29 | |||
30 | Eicon DIVA Server PRI/PCI | ||
31 | - full support on amount of B-channels | ||
32 | depending on DSPs on board. | ||
33 | |||
34 | |||
35 | |||
36 | The command set is known as Class 2 (not Class 2.0) and | ||
37 | can be activated by AT+FCLASS=2 | ||
38 | |||
39 | |||
40 | The interface between the link-level-module and the hardware-level driver | ||
41 | is described in the files INTERFACE.fax and INTERFACE. | ||
42 | |||
43 | Armin | ||
44 | mac@melware.de | ||
45 | |||
diff --git a/Documentation/isdn/README.hfc-pci b/Documentation/isdn/README.hfc-pci deleted file mode 100644 index e8a4ef0226e8..000000000000 --- a/Documentation/isdn/README.hfc-pci +++ /dev/null | |||
@@ -1,41 +0,0 @@ | |||
1 | The driver for the HFC-PCI and HFC-PCI-A chips from CCD may be used | ||
2 | for many OEM cards using this chips. | ||
3 | Additionally the driver has a special feature which makes it possible | ||
4 | to read the echo-channel of the isdn bus. So all frames in both directions | ||
5 | may be logged. | ||
6 | When the echo logging feature is used the number of available B-channels | ||
7 | for a HFC-PCI card is reduced to 1. Of course this is only relevant to | ||
8 | the card, not to the isdn line. | ||
9 | To activate the echo mode the following ioctls must be entered: | ||
10 | |||
11 | hisaxctrl <driver/cardname> 10 1 | ||
12 | |||
13 | This reduces the available channels to 1. There must not be open connections | ||
14 | through this card when entering the command. | ||
15 | And then: | ||
16 | |||
17 | hisaxctrl <driver/cardname> 12 1 | ||
18 | |||
19 | This enables the echo mode. If Hex logging is activated the isdnctrlx | ||
20 | devices show a output with a line beginning of HEX: for the providers | ||
21 | exchange and ECHO: for isdn devices sending to the provider. | ||
22 | |||
23 | If more than one HFC-PCI cards are installed, a specific card may be selected | ||
24 | at the hisax module load command line. Supply the load command with the desired | ||
25 | IO-address of the desired card. | ||
26 | Example: | ||
27 | There tree cards installed in your machine at IO-base addresses 0xd000, 0xd400 | ||
28 | and 0xdc00 | ||
29 | If you want to use the card at 0xd400 standalone you should supply the insmod | ||
30 | or depmod with type=35 io=0xd400. | ||
31 | If you want to use all three cards, but the order needs to be at 0xdc00,0xd400, | ||
32 | 0xd000 you may give the parameters type=35,35,35 io=0xdc00,0xd400,0xd00 | ||
33 | Then the desired card will be the initialised in the desired order. | ||
34 | If the io parameter is used the io addresses of all used cards should be | ||
35 | supplied else the parameter is assumed 0 and a auto search for a free card is | ||
36 | invoked which may not give the wanted result. | ||
37 | |||
38 | Comments and reports to werner@isdn4linux.de or werner@isdn-development.de | ||
39 | |||
40 | |||
41 | |||
diff --git a/Documentation/isdn/README.syncppp b/Documentation/isdn/README.syncppp deleted file mode 100644 index 27d260095cce..000000000000 --- a/Documentation/isdn/README.syncppp +++ /dev/null | |||
@@ -1,58 +0,0 @@ | |||
1 | Some additional information for setting up a syncPPP | ||
2 | connection using network interfaces. | ||
3 | --------------------------------------------------------------- | ||
4 | |||
5 | You need one thing beside the isdn4linux package: | ||
6 | |||
7 | a patched pppd .. (I called it ipppd to show the difference) | ||
8 | |||
9 | Compiling isdn4linux with sync PPP: | ||
10 | ----------------------------------- | ||
11 | To compile isdn4linux with the sync PPP part, you have | ||
12 | to answer the appropriate question when doing a "make config" | ||
13 | Don't forget to load the slhc.o | ||
14 | module before the isdn.o module, if VJ-compression support | ||
15 | is not compiled into your kernel. (e.g if you have no PPP or | ||
16 | CSLIP in the kernel) | ||
17 | |||
18 | Using isdn4linux with sync PPP: | ||
19 | ------------------------------- | ||
20 | Sync PPP is just another encapsulation for isdn4linux. The | ||
21 | name to enable sync PPP encapsulation is 'syncppp' .. e.g: | ||
22 | |||
23 | /sbin/isdnctrl encap ippp0 syncppp | ||
24 | |||
25 | The name of the interface is here 'ippp0'. You need | ||
26 | one interface with the name 'ippp0' to saturate the | ||
27 | ipppd, which checks the ppp version via this interface. | ||
28 | Currently, all devices must have the name ipppX where | ||
29 | 'X' is a decimal value. | ||
30 | |||
31 | To set up a PPP connection you need the ipppd .. You must start | ||
32 | the ipppd once after installing the modules. The ipppd | ||
33 | communicates with the isdn4linux link-level driver using the | ||
34 | /dev/ippp0 to /dev/ippp15 devices. One ipppd can handle | ||
35 | all devices at once. If you want to use two PPP connections | ||
36 | at the same time, you have to connect the ipppd to two | ||
37 | devices .. and so on. | ||
38 | I've implemented one additional option for the ipppd: | ||
39 | 'useifip' will get (if set to not 0.0.0.0) the IP address | ||
40 | for the negotiation from the attached network-interface. | ||
41 | (also: ipppd will try to negotiate pointopoint IP as remote IP) | ||
42 | You must disable BSD-compression, this implementation can't | ||
43 | handle compressed packets. | ||
44 | |||
45 | Check the etc/rc.isdn.syncppp in the isdn4kernel-util package | ||
46 | for an example setup script. | ||
47 | |||
48 | To use the MPPP stuff, you must configure a slave device | ||
49 | with isdn4linux. Now call the ipppd with the '+mp' option. | ||
50 | To increase the number of links, you must use the | ||
51 | 'addlink' option of the isdnctrl tool. (rc.isdn.syncppp.MPPP is | ||
52 | an example script) | ||
53 | |||
54 | enjoy it, | ||
55 | michael | ||
56 | |||
57 | |||
58 | |||
diff --git a/Documentation/isdn/README.x25 b/Documentation/isdn/README.x25 deleted file mode 100644 index e561a77c4e22..000000000000 --- a/Documentation/isdn/README.x25 +++ /dev/null | |||
@@ -1,184 +0,0 @@ | |||
1 | |||
2 | X.25 support within isdn4linux | ||
3 | ============================== | ||
4 | |||
5 | This is alpha/beta test code. Use it completely at your own risk. | ||
6 | As new versions appear, the stuff described here might suddenly change | ||
7 | or become invalid without notice. | ||
8 | |||
9 | Keep in mind: | ||
10 | |||
11 | You are using several new parts of the 2.2.x kernel series which | ||
12 | have not been tested in a large scale. Therefore, you might encounter | ||
13 | more bugs as usual. | ||
14 | |||
15 | - If you connect to an X.25 neighbour not operated by yourself, ASK the | ||
16 | other side first. Be prepared that bugs in the protocol implementation | ||
17 | might result in problems. | ||
18 | |||
19 | - This implementation has never wiped out my whole hard disk yet. But as | ||
20 | this is experimental code, don't blame me if that happened to you. | ||
21 | Backing up important data will never harm. | ||
22 | |||
23 | - Monitor your isdn connections while using this software. This should | ||
24 | prevent you from undesired phone bills in case of driver problems. | ||
25 | |||
26 | |||
27 | |||
28 | |||
29 | How to configure the kernel | ||
30 | =========================== | ||
31 | |||
32 | The ITU-T (former CCITT) X.25 network protocol layer has been implemented | ||
33 | in the Linux source tree since version 2.1.16. The isdn subsystem might be | ||
34 | useful to run X.25 on top of ISDN. If you want to try it, select | ||
35 | |||
36 | "CCITT X.25 Packet Layer" | ||
37 | |||
38 | from the networking options as well as | ||
39 | |||
40 | "ISDN Support" and "X.25 PLP on Top of ISDN" | ||
41 | |||
42 | from the ISDN subsystem options when you configure your kernel for | ||
43 | compilation. You currently also need to enable | ||
44 | "Prompt for development and/or incomplete code/drivers" from the | ||
45 | "Code maturity level options" menu. For the x25trace utility to work | ||
46 | you also need to enable "Packet socket". | ||
47 | |||
48 | For local testing it is also recommended to enable the isdnloop driver | ||
49 | from the isdn subsystem's configuration menu. | ||
50 | |||
51 | For testing, it is recommended that all isdn drivers and the X.25 PLP | ||
52 | protocol are compiled as loadable modules. Like this, you can recover | ||
53 | from certain errors by simply unloading and reloading the modules. | ||
54 | |||
55 | |||
56 | |||
57 | What's it for? How to use it? | ||
58 | ============================= | ||
59 | |||
60 | X.25 on top of isdn might be useful with two different scenarios: | ||
61 | |||
62 | - You might want to access a public X.25 data network from your Linux box. | ||
63 | You can use i4l if you were physically connected to the X.25 switch | ||
64 | by an ISDN B-channel (leased line as well as dial up connection should | ||
65 | work). | ||
66 | |||
67 | This corresponds to ITU-T recommendation X.31 Case A (circuit-mode | ||
68 | access to PSPDN [packet switched public data network]). | ||
69 | |||
70 | NOTE: X.31 also covers a Case B (access to PSPDN via virtual | ||
71 | circuit / packet mode service). The latter mode (which in theory | ||
72 | also allows using the D-channel) is not supported by isdn4linux. | ||
73 | It should however be possible to establish such packet mode connections | ||
74 | with certain active isdn cards provided that the firmware supports X.31 | ||
75 | and the driver exports this functionality to the user. Currently, | ||
76 | the AVM B1 driver is the only driver which does so. (It should be | ||
77 | possible to access D-channel X.31 with active AVM cards using the | ||
78 | CAPI interface of the AVM-B1 driver). | ||
79 | |||
80 | - Or you might want to operate certain ISDN teleservices on your linux | ||
81 | box. A lot of those teleservices run on top of the ISO-8208 | ||
82 | (DTE-DTE mode) network layer protocol. ISO-8208 is essentially the | ||
83 | same as ITU-T X.25. | ||
84 | |||
85 | Popular candidates of such teleservices are EUROfile transfer or any | ||
86 | teleservice applying ITU-T recommendation T.90. | ||
87 | |||
88 | To use the X.25 protocol on top of isdn, just create an isdn network | ||
89 | interface as usual, configure your own and/or peer's ISDN numbers, | ||
90 | and choose x25iface encapsulation by | ||
91 | |||
92 | isdnctrl encap <iface-name> x25iface. | ||
93 | |||
94 | Once encap is set like this, the device can be used by the X.25 packet layer. | ||
95 | |||
96 | All the stuff needed for X.25 is implemented inside the isdn link | ||
97 | level (mainly isdn_net.c and some new source files). Thus, it should | ||
98 | work with every existing HL driver. I was able to successfully open X.25 | ||
99 | connections on top of the isdnloop driver and the hisax driver. | ||
100 | "x25iface"-encapsulation bypasses demand dialing. Dialing will be | ||
101 | initiated when the upper (X.25 packet) layer requests the lapb datalink to | ||
102 | be established. But hangup timeout is still active. Whenever a hangup | ||
103 | occurs, all existing X.25 connections on that link will be cleared | ||
104 | It is recommended to use sufficiently large hangup-timeouts for the | ||
105 | isdn interfaces. | ||
106 | |||
107 | |||
108 | In order to set up a conforming protocol stack you also need to | ||
109 | specify the proper l2_prot parameter: | ||
110 | |||
111 | To operate in ISO-8208 X.25 DTE-DTE mode, use | ||
112 | |||
113 | isdnctrl l2_prot <iface-name> x75i | ||
114 | |||
115 | To access an X.25 network switch via isdn (your linux box is the DTE), use | ||
116 | |||
117 | isdnctrl l2_prot <iface-name> x25dte | ||
118 | |||
119 | To mimic an X.25 network switch (DCE side of the connection), use | ||
120 | |||
121 | isdnctrl l2_prot <iface-name> x25dce | ||
122 | |||
123 | However, x25dte or x25dce is currently not supported by any real HL | ||
124 | level driver. The main difference between x75i and x25dte/dce is that | ||
125 | x25d[tc]e uses fixed lap_b addresses. With x75i, the side which | ||
126 | initiates the isdn connection uses the DTE's lap_b address while the | ||
127 | called side used the DCE's lap_b address. Thus, l2_prot x75i might | ||
128 | probably work if you access a public X.25 network as long as the | ||
129 | corresponding isdn connection is set up by you. At least one test | ||
130 | was successful to connect via isdn4linux to an X.25 switch using this | ||
131 | trick. At the switch side, a terminal adapter X.21 was used to connect | ||
132 | it to the isdn. | ||
133 | |||
134 | |||
135 | How to set up a test installation? | ||
136 | ================================== | ||
137 | |||
138 | To test X.25 on top of isdn, you need to get | ||
139 | |||
140 | - a recent version of the "isdnctrl" program that supports setting the new | ||
141 | X.25 specific parameters. | ||
142 | |||
143 | - the x25-utils-2.X package from | ||
144 | ftp://ftp.hes.iki.fi/pub/ham/linux/ax25/x25utils-* | ||
145 | (don't confuse the x25-utils with the ax25-utils) | ||
146 | |||
147 | - an application program that uses linux PF_X25 sockets (some are | ||
148 | contained in the x25-util package). | ||
149 | |||
150 | Before compiling the user level utilities make sure that the compiler/ | ||
151 | preprocessor will fetch the proper kernel header files of this kernel | ||
152 | source tree. Either make /usr/include/linux a symbolic link pointing to | ||
153 | this kernel's include/linux directory or set the appropriate compiler flags. | ||
154 | |||
155 | When all drivers and interfaces are loaded and configured you need to | ||
156 | ifconfig the network interfaces up and add X.25-routes to them. Use | ||
157 | the usual ifconfig tool. | ||
158 | |||
159 | ifconfig <iface-name> up | ||
160 | |||
161 | But a special x25route tool (distributed with the x25-util package) | ||
162 | is needed to set up X.25 routes. I.e. | ||
163 | |||
164 | x25route add 01 <iface-name> | ||
165 | |||
166 | will cause all x.25 connections to the destination X.25-address | ||
167 | "01" to be routed to your created isdn network interface. | ||
168 | |||
169 | There are currently no real X.25 applications available. However, for | ||
170 | tests, the x25-utils package contains a modified version of telnet | ||
171 | and telnetd that uses X.25 sockets instead of tcp/ip sockets. You can | ||
172 | use those for your first tests. Furthermore, you might check | ||
173 | ftp://ftp.hamburg.pop.de/pub/LOCAL/linux/i4l-eft/ which contains some | ||
174 | alpha-test implementation ("eftp4linux") of the EUROfile transfer | ||
175 | protocol. | ||
176 | |||
177 | The scripts distributed with the eftp4linux test releases might also | ||
178 | provide useful examples for setting up X.25 on top of isdn. | ||
179 | |||
180 | The x25-utility package also contains an x25trace tool that can be | ||
181 | used to monitor X.25 packets received by the network interfaces. | ||
182 | The /proc/net/x25* files also contain useful information. | ||
183 | |||
184 | - Henner | ||
diff --git a/Documentation/isdn/syncPPP.FAQ b/Documentation/isdn/syncPPP.FAQ deleted file mode 100644 index 3257a4bc0786..000000000000 --- a/Documentation/isdn/syncPPP.FAQ +++ /dev/null | |||
@@ -1,224 +0,0 @@ | |||
1 | simple isdn4linux PPP FAQ .. to be continued .. not 'debugged' | ||
2 | ------------------------------------------------------------------- | ||
3 | |||
4 | Q01: what's pppd, ipppd, syncPPP, asyncPPP ?? | ||
5 | Q02: error message "this system lacks PPP support" | ||
6 | Q03: strange information using 'ifconfig' | ||
7 | Q04: MPPP?? What's that and how can I use it ... | ||
8 | Q05: I tried MPPP but it doesn't work | ||
9 | Q06: can I use asynchronous PPP encapsulation with network devices | ||
10 | Q07: A SunISDN machine can't connect to my i4l system | ||
11 | Q08: I wanna talk to several machines, which need different configs | ||
12 | Q09: Starting the ipppd, I get only error messages from i4l | ||
13 | Q10: I wanna use dynamic IP address assignment | ||
14 | Q11: I can't connect. How can I check where the problem is. | ||
15 | Q12: How can I reduce login delay? | ||
16 | |||
17 | ------------------------------------------------------------------- | ||
18 | |||
19 | Q01: pppd, ipppd, syncPPP, asyncPPP .. what is that ? | ||
20 | what should I use? | ||
21 | A: The pppd is for asynchronous PPP .. asynchronous means | ||
22 | here, the framing is character based. (e.g when | ||
23 | using ttyI* or tty* devices) | ||
24 | |||
25 | The ipppd handles PPP packets coming in HDLC | ||
26 | frames (bit based protocol) ... The PPP driver | ||
27 | in isdn4linux pushes all IP packets direct | ||
28 | to the network layer and all PPP protocol | ||
29 | frames to the /dev/ippp* device. | ||
30 | So, the ipppd is a simple external network | ||
31 | protocol handler. | ||
32 | |||
33 | If you login into a remote machine using the | ||
34 | /dev/ttyI* devices and then enable PPP on the | ||
35 | remote terminal server -> use the 'old' pppd | ||
36 | |||
37 | If your remote side immediately starts to send | ||
38 | frames ... you probably connect to a | ||
39 | syncPPP machine .. use the network device part | ||
40 | of isdn4linux with the 'syncppp' encapsulation | ||
41 | and make sure, that the ipppd is running and | ||
42 | connected to at least one /dev/ippp*. Check the | ||
43 | isdn4linux manual on how to configure a network device. | ||
44 | |||
45 | -- | ||
46 | |||
47 | Q02: when I start the ipppd .. I only get the | ||
48 | error message "this system lacks PPP support" | ||
49 | A: check that at least the device 'ippp0' exists. | ||
50 | (you can check this e.g with the program 'ifconfig') | ||
51 | The ipppd NEEDS this device under THIS name .. | ||
52 | If this device doesn't exists, use: | ||
53 | isdnctrl addif ippp0 | ||
54 | isdnctrl encap ippp0 syncppp | ||
55 | ... (see isdn4linux doc for more) ... | ||
56 | A: Maybe you have compiled the ipppd with another | ||
57 | kernel source tree than the kernel you currently | ||
58 | run ... | ||
59 | |||
60 | -- | ||
61 | |||
62 | Q03: when I list the netdevices with ifconfig I see, that | ||
63 | my ISDN interface has a HWaddr and IRQ=0 and Base | ||
64 | address = 0 | ||
65 | A: The device is a fake ethernet device .. ignore IRQ and baseaddr | ||
66 | You need the HWaddr only for ethernet encapsulation. | ||
67 | |||
68 | -- | ||
69 | |||
70 | Q04: MPPP?? What's that and how can I use it ... | ||
71 | |||
72 | A: MPPP or MP or MPP (Warning: MP is also an | ||
73 | acronym for 'Multi Processor') stands for | ||
74 | Multi Point to Point and means bundling | ||
75 | of several channels to one logical stream. | ||
76 | To enable MPPP negotiation you must call the | ||
77 | ipppd with the '+mp' option. | ||
78 | You must also configure a slave device for | ||
79 | every additional channel. (see the i4l manual | ||
80 | for more) | ||
81 | To use channel bundling you must first activate | ||
82 | the 'master' or initial call. Now you can add | ||
83 | the slave channels with the command: | ||
84 | isdnctrl addlink <device> | ||
85 | e.g: | ||
86 | isdnctrl addlink ippp0 | ||
87 | This is different from other encapsulations of | ||
88 | isdn4linux! With syncPPP, there is no automatic | ||
89 | activation of slave devices. | ||
90 | |||
91 | -- | ||
92 | |||
93 | Q05: I tried MPPP but it doesn't work .. the ipppd | ||
94 | writes in the debug log something like: | ||
95 | .. rcvd [0][proto=0x3d] c0 00 00 00 80 fd 01 01 00 0a ... | ||
96 | .. sent [0][LCP ProtRej id=0x2 00 3d c0 00 00 00 80 fd 01 ... | ||
97 | |||
98 | A: you forgot to compile MPPP/RFC1717 support into the | ||
99 | ISDN Subsystem. Recompile with this option enabled. | ||
100 | |||
101 | -- | ||
102 | |||
103 | Q06: can I use asynchronous PPP encapsulation | ||
104 | over the network interface of isdn4linux .. | ||
105 | |||
106 | A: No .. that's not possible .. Use the standard | ||
107 | PPP package over the /dev/ttyI* devices. You | ||
108 | must not use the ipppd for this. | ||
109 | |||
110 | -- | ||
111 | |||
112 | Q07: A SunISDN machine tries to connect my i4l system, | ||
113 | which doesn't work. | ||
114 | Checking the debug log I just saw garbage like: | ||
115 | !![ ... fill in the line ... ]!! | ||
116 | |||
117 | A: The Sun tries to talk asynchronous PPP ... i4l | ||
118 | can't understand this ... try to use the ttyI* | ||
119 | devices with the standard PPP/pppd package | ||
120 | |||
121 | A: (from Alexanter Strauss: ) | ||
122 | !![ ... fill in mail ]!! | ||
123 | |||
124 | -- | ||
125 | |||
126 | Q08: I wanna talk to remote machines, which need | ||
127 | a different configuration. The only way | ||
128 | I found to do this is to kill the ipppd and | ||
129 | start a new one with another config to connect | ||
130 | to the second machine. | ||
131 | |||
132 | A: you must bind a network interface explicitly to | ||
133 | an ippp device, where you can connect a (for this | ||
134 | interface) individually configured ipppd. | ||
135 | |||
136 | -- | ||
137 | |||
138 | Q09: When I start the ipppd I only get error messages | ||
139 | from the i4l driver .. | ||
140 | |||
141 | A: When starting, the ipppd calls functions which may | ||
142 | trigger a network packet. (e.g gethostbyname()). | ||
143 | Without the ipppd (at this moment, it is not | ||
144 | fully started) we can't handle this network request. | ||
145 | Try to configure hostnames necessary for the ipppd | ||
146 | in your local /etc/hosts file or in a way, that | ||
147 | your system can resolve it without using an | ||
148 | isdn/ippp network-interface. | ||
149 | |||
150 | -- | ||
151 | |||
152 | Q10: I wanna use dynamic IP address assignment ... How | ||
153 | must I configure the network device. | ||
154 | |||
155 | A: At least you must have a route which forwards | ||
156 | a packet to the ippp network-interface to trigger | ||
157 | the dial-on-demand. | ||
158 | A default route to the ippp-interface will work. | ||
159 | Now you must choose a dummy IP address for your | ||
160 | interface. | ||
161 | If for some reason you can't set the default | ||
162 | route to the ippp interface, you may take any | ||
163 | address of the subnet from which you expect your | ||
164 | dynamic IP number and set a 'network route' for | ||
165 | this subnet to the ippp interface. | ||
166 | To allow overriding of the dummy address you | ||
167 | must call the ipppd with the 'ipcp-accept-local' option. | ||
168 | |||
169 | A: You must know, how the ipppd gets the addresses it wanna | ||
170 | configure. If you don't give any option, the ipppd | ||
171 | tries to negotiate the local host address! | ||
172 | With the option 'noipdefault' it requests an address | ||
173 | from the remote machine. With 'useifip' it gets the | ||
174 | addresses from the net interface. Or you set the address | ||
175 | on the option line with the <a.b.c.d:e.f.g.h> option. | ||
176 | Note: the IP address of the remote machine must be configured | ||
177 | locally or the remote machine must send it in an IPCP request. | ||
178 | If your side doesn't know the IP address after negotiation, it | ||
179 | closes the connection! | ||
180 | You must allow overriding of address with the 'ipcp-accept-*' | ||
181 | options, if you have set your own or the remote address | ||
182 | explicitly. | ||
183 | |||
184 | A: Maybe you try these options .. e.g: | ||
185 | |||
186 | /sbin/ipppd :$REMOTE noipdefault /dev/ippp0 | ||
187 | |||
188 | where REMOTE must be the address of the remote machine (the | ||
189 | machine, which gives you your address) | ||
190 | |||
191 | -- | ||
192 | |||
193 | Q11: I can't connect. How can I check where the problem is. | ||
194 | |||
195 | A: A good help log is the debug output from the ipppd... | ||
196 | Check whether you can find there: | ||
197 | - only a few LCP-conf-req SENT messages (less then 10) | ||
198 | and then a Term-REQ: | ||
199 | -> check whether your ISDN card is well configured | ||
200 | it seems, that your machine doesn't dial | ||
201 | (IRQ,IO,Proto, etc problems) | ||
202 | Configure your ISDN card to print debug messages and | ||
203 | check the /dev/isdnctrl output next time. There | ||
204 | you can see, whether there is activity on the card/line. | ||
205 | - there are at least a few RECV messages in the log: | ||
206 | -> fine: your card is dialing and your remote machine | ||
207 | tries to talk with you. Maybe only a missing | ||
208 | authentication. Check your ipppd configuration again. | ||
209 | - the ipppd exits for some reason: | ||
210 | -> not good ... check /var/adm/syslog and /var/adm/daemon. | ||
211 | Could be a bug in the ipppd. | ||
212 | |||
213 | -- | ||
214 | |||
215 | Q12: How can I reduce login delay? | ||
216 | |||
217 | A: Log a login session ('debug' log) and check which options | ||
218 | your remote side rejects. Next time configure your ipppd | ||
219 | to not negotiate these options. Another 'side effect' is, that | ||
220 | this increases redundancy. (e.g your remote side is buggy and | ||
221 | rejects options in a wrong way). | ||
222 | |||
223 | |||
224 | |||
diff --git a/Documentation/process/changes.rst b/Documentation/process/changes.rst index 18735dc460a0..111636ad1bad 100644 --- a/Documentation/process/changes.rst +++ b/Documentation/process/changes.rst | |||
@@ -23,8 +23,8 @@ running, the suggested command should tell you. | |||
23 | 23 | ||
24 | Again, keep in mind that this list assumes you are already functionally | 24 | Again, keep in mind that this list assumes you are already functionally |
25 | running a Linux kernel. Also, not all tools are necessary on all | 25 | running a Linux kernel. Also, not all tools are necessary on all |
26 | systems; obviously, if you don't have any ISDN hardware, for example, | 26 | systems; obviously, if you don't have any PC Card hardware, for example, |
27 | you probably needn't concern yourself with isdn4k-utils. | 27 | you probably needn't concern yourself with pcmciautils. |
28 | 28 | ||
29 | ====================== =============== ======================================== | 29 | ====================== =============== ======================================== |
30 | Program Minimal version Command to check the version | 30 | Program Minimal version Command to check the version |
@@ -45,7 +45,6 @@ btrfs-progs 0.18 btrfsck | |||
45 | pcmciautils 004 pccardctl -V | 45 | pcmciautils 004 pccardctl -V |
46 | quota-tools 3.09 quota -V | 46 | quota-tools 3.09 quota -V |
47 | PPP 2.4.0 pppd --version | 47 | PPP 2.4.0 pppd --version |
48 | isdn4k-utils 3.1pre1 isdnctrl 2>&1|grep version | ||
49 | nfs-utils 1.0.5 showmount --version | 48 | nfs-utils 1.0.5 showmount --version |
50 | procps 3.2.0 ps --version | 49 | procps 3.2.0 ps --version |
51 | oprofile 0.9 oprofiled --version | 50 | oprofile 0.9 oprofiled --version |
@@ -279,12 +278,6 @@ which can be made by:: | |||
279 | 278 | ||
280 | as root. | 279 | as root. |
281 | 280 | ||
282 | Isdn4k-utils | ||
283 | ------------ | ||
284 | |||
285 | Due to changes in the length of the phone number field, isdn4k-utils | ||
286 | needs to be recompiled or (preferably) upgraded. | ||
287 | |||
288 | NFS-utils | 281 | NFS-utils |
289 | --------- | 282 | --------- |
290 | 283 | ||
@@ -448,11 +441,6 @@ PPP | |||
448 | 441 | ||
449 | - <ftp://ftp.samba.org/pub/ppp/> | 442 | - <ftp://ftp.samba.org/pub/ppp/> |
450 | 443 | ||
451 | Isdn4k-utils | ||
452 | ------------ | ||
453 | |||
454 | - <ftp://ftp.isdn4linux.de/pub/isdn4linux/utils/> | ||
455 | |||
456 | NFS-utils | 444 | NFS-utils |
457 | --------- | 445 | --------- |
458 | 446 | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 0c55b0fedbe2..3a761e680296 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -8371,9 +8371,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/kkeil/isdn-2.6.git | |||
8371 | S: Maintained | 8371 | S: Maintained |
8372 | F: Documentation/isdn/ | 8372 | F: Documentation/isdn/ |
8373 | F: drivers/isdn/ | 8373 | F: drivers/isdn/ |
8374 | F: include/linux/isdn.h | ||
8375 | F: include/linux/isdn/ | 8374 | F: include/linux/isdn/ |
8376 | F: include/uapi/linux/isdn.h | ||
8377 | F: include/uapi/linux/isdn/ | 8375 | F: include/uapi/linux/isdn/ |
8378 | 8376 | ||
8379 | IT87 HARDWARE MONITORING DRIVER | 8377 | IT87 HARDWARE MONITORING DRIVER |
diff --git a/drivers/isdn/Kconfig b/drivers/isdn/Kconfig index 1ca4d70d198a..6e3bf833c67e 100644 --- a/drivers/isdn/Kconfig +++ b/drivers/isdn/Kconfig | |||
@@ -21,27 +21,6 @@ menuconfig ISDN | |||
21 | 21 | ||
22 | if ISDN | 22 | if ISDN |
23 | 23 | ||
24 | menuconfig ISDN_I4L | ||
25 | tristate "Old ISDN4Linux (deprecated)" | ||
26 | depends on TTY | ||
27 | ---help--- | ||
28 | This driver allows you to use an ISDN adapter for networking | ||
29 | connections and as dialin/out device. The isdn-tty's have a built | ||
30 | in AT-compatible modem emulator. Network devices support autodial, | ||
31 | channel-bundling, callback and caller-authentication without having | ||
32 | a daemon running. A reduced T.70 protocol is supported with tty's | ||
33 | suitable for German BTX. On D-Channel, the protocols EDSS1 | ||
34 | (Euro-ISDN) and 1TR6 (German style) are supported. See | ||
35 | <file:Documentation/isdn/README> for more information. | ||
36 | |||
37 | ISDN support in the linux kernel is moving towards a new API, | ||
38 | called CAPI (Common ISDN Application Programming Interface). | ||
39 | Therefore the old ISDN4Linux layer will eventually become obsolete. | ||
40 | It is still available, though, for use with adapters that are not | ||
41 | supported by the new CAPI subsystem yet. | ||
42 | |||
43 | source "drivers/isdn/i4l/Kconfig" | ||
44 | |||
45 | menuconfig ISDN_CAPI | 24 | menuconfig ISDN_CAPI |
46 | tristate "CAPI 2.0 subsystem" | 25 | tristate "CAPI 2.0 subsystem" |
47 | help | 26 | help |
@@ -71,9 +50,4 @@ source "drivers/isdn/hysdn/Kconfig" | |||
71 | 50 | ||
72 | source "drivers/isdn/mISDN/Kconfig" | 51 | source "drivers/isdn/mISDN/Kconfig" |
73 | 52 | ||
74 | config ISDN_HDLC | ||
75 | tristate | ||
76 | select CRC_CCITT | ||
77 | select BITREVERSE | ||
78 | |||
79 | endif # ISDN | 53 | endif # ISDN |
diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile index 7487f0bbe855..379b4a03c321 100644 --- a/drivers/isdn/Makefile +++ b/drivers/isdn/Makefile | |||
@@ -7,7 +7,5 @@ obj-$(CONFIG_ISDN_I4L) += i4l/ | |||
7 | obj-$(CONFIG_ISDN_CAPI) += capi/ | 7 | obj-$(CONFIG_ISDN_CAPI) += capi/ |
8 | obj-$(CONFIG_MISDN) += mISDN/ | 8 | obj-$(CONFIG_MISDN) += mISDN/ |
9 | obj-$(CONFIG_ISDN) += hardware/ | 9 | obj-$(CONFIG_ISDN) += hardware/ |
10 | obj-$(CONFIG_ISDN_DIVERSION) += divert/ | ||
11 | obj-$(CONFIG_ISDN_DRV_LOOP) += isdnloop/ | ||
12 | obj-$(CONFIG_HYSDN) += hysdn/ | 10 | obj-$(CONFIG_HYSDN) += hysdn/ |
13 | obj-$(CONFIG_ISDN_DRV_GIGASET) += gigaset/ | 11 | obj-$(CONFIG_ISDN_DRV_GIGASET) += gigaset/ |
diff --git a/drivers/isdn/capi/Kconfig b/drivers/isdn/capi/Kconfig index abaadce376c5..089dbee18f36 100644 --- a/drivers/isdn/capi/Kconfig +++ b/drivers/isdn/capi/Kconfig | |||
@@ -27,15 +27,6 @@ config ISDN_CAPI_MIDDLEWARE | |||
27 | device. If you want to use pppd with pppdcapiplugin to dial up to | 27 | device. If you want to use pppd with pppdcapiplugin to dial up to |
28 | your ISP, say Y here. | 28 | your ISP, say Y here. |
29 | 29 | ||
30 | config ISDN_CAPI_CAPIDRV | ||
31 | tristate "CAPI2.0 capidrv interface support" | ||
32 | depends on ISDN_I4L | ||
33 | help | ||
34 | This option provides the glue code to hook up CAPI driven cards to | ||
35 | the legacy isdn4linux link layer. If you have a card which is | ||
36 | supported by a CAPI driver, but still want to use old features like | ||
37 | ippp interfaces or ttyI emulation, say Y/M here. | ||
38 | |||
39 | config ISDN_CAPI_CAPIDRV_VERBOSE | 30 | config ISDN_CAPI_CAPIDRV_VERBOSE |
40 | bool "Verbose reason code reporting" | 31 | bool "Verbose reason code reporting" |
41 | depends on ISDN_CAPI_CAPIDRV | 32 | depends on ISDN_CAPI_CAPIDRV |
diff --git a/drivers/isdn/capi/capidrv.c b/drivers/isdn/capi/capidrv.c deleted file mode 100644 index e8949f3dcae1..000000000000 --- a/drivers/isdn/capi/capidrv.c +++ /dev/null | |||
@@ -1,2525 +0,0 @@ | |||
1 | /* $Id: capidrv.c,v 1.1.2.2 2004/01/12 23:17:24 keil Exp $ | ||
2 | * | ||
3 | * ISDN4Linux Driver, using capi20 interface (kernelcapi) | ||
4 | * | ||
5 | * Copyright 1997 by Carsten Paeth <calle@calle.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/compiler.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/major.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/fcntl.h> | ||
19 | #include <linux/fs.h> | ||
20 | #include <linux/signal.h> | ||
21 | #include <linux/mm.h> | ||
22 | #include <linux/timer.h> | ||
23 | #include <linux/wait.h> | ||
24 | #include <linux/skbuff.h> | ||
25 | #include <linux/isdn.h> | ||
26 | #include <linux/isdnif.h> | ||
27 | #include <linux/proc_fs.h> | ||
28 | #include <linux/seq_file.h> | ||
29 | #include <linux/capi.h> | ||
30 | #include <linux/kernelcapi.h> | ||
31 | #include <linux/ctype.h> | ||
32 | #include <linux/init.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | |||
35 | #include <linux/isdn/capiutil.h> | ||
36 | #include <linux/isdn/capicmd.h> | ||
37 | #include "capidrv.h" | ||
38 | |||
39 | static int debugmode = 0; | ||
40 | |||
41 | MODULE_DESCRIPTION("CAPI4Linux: Interface to ISDN4Linux"); | ||
42 | MODULE_AUTHOR("Carsten Paeth"); | ||
43 | MODULE_LICENSE("GPL"); | ||
44 | module_param(debugmode, uint, S_IRUGO | S_IWUSR); | ||
45 | |||
46 | /* -------- type definitions ----------------------------------------- */ | ||
47 | |||
48 | |||
49 | struct capidrv_contr { | ||
50 | |||
51 | struct capidrv_contr *next; | ||
52 | struct module *owner; | ||
53 | u32 contrnr; | ||
54 | char name[20]; | ||
55 | |||
56 | /* | ||
57 | * for isdn4linux | ||
58 | */ | ||
59 | isdn_if interface; | ||
60 | int myid; | ||
61 | |||
62 | /* | ||
63 | * LISTEN state | ||
64 | */ | ||
65 | int state; | ||
66 | u32 cipmask; | ||
67 | u32 cipmask2; | ||
68 | struct timer_list listentimer; | ||
69 | |||
70 | /* | ||
71 | * ID of capi message sent | ||
72 | */ | ||
73 | u16 msgid; | ||
74 | |||
75 | /* | ||
76 | * B-Channels | ||
77 | */ | ||
78 | int nbchan; | ||
79 | struct capidrv_bchan { | ||
80 | struct capidrv_contr *contr; | ||
81 | u8 msn[ISDN_MSNLEN]; | ||
82 | int l2; | ||
83 | int l3; | ||
84 | u8 num[ISDN_MSNLEN]; | ||
85 | u8 mynum[ISDN_MSNLEN]; | ||
86 | int si1; | ||
87 | int si2; | ||
88 | int incoming; | ||
89 | int disconnecting; | ||
90 | struct capidrv_plci { | ||
91 | struct capidrv_plci *next; | ||
92 | u32 plci; | ||
93 | u32 ncci; /* ncci for CONNECT_ACTIVE_IND */ | ||
94 | u16 msgid; /* to identfy CONNECT_CONF */ | ||
95 | int chan; | ||
96 | int state; | ||
97 | int leasedline; | ||
98 | struct capidrv_ncci { | ||
99 | struct capidrv_ncci *next; | ||
100 | struct capidrv_plci *plcip; | ||
101 | u32 ncci; | ||
102 | u16 msgid; /* to identfy CONNECT_B3_CONF */ | ||
103 | int chan; | ||
104 | int state; | ||
105 | int oldstate; | ||
106 | /* */ | ||
107 | u16 datahandle; | ||
108 | struct ncci_datahandle_queue { | ||
109 | struct ncci_datahandle_queue *next; | ||
110 | u16 datahandle; | ||
111 | int len; | ||
112 | } *ackqueue; | ||
113 | } *ncci_list; | ||
114 | } *plcip; | ||
115 | struct capidrv_ncci *nccip; | ||
116 | } *bchans; | ||
117 | |||
118 | struct capidrv_plci *plci_list; | ||
119 | |||
120 | /* for q931 data */ | ||
121 | u8 q931_buf[4096]; | ||
122 | u8 *q931_read; | ||
123 | u8 *q931_write; | ||
124 | u8 *q931_end; | ||
125 | }; | ||
126 | |||
127 | |||
128 | struct capidrv_data { | ||
129 | struct capi20_appl ap; | ||
130 | int ncontr; | ||
131 | struct capidrv_contr *contr_list; | ||
132 | }; | ||
133 | |||
134 | typedef struct capidrv_plci capidrv_plci; | ||
135 | typedef struct capidrv_ncci capidrv_ncci; | ||
136 | typedef struct capidrv_contr capidrv_contr; | ||
137 | typedef struct capidrv_data capidrv_data; | ||
138 | typedef struct capidrv_bchan capidrv_bchan; | ||
139 | |||
140 | /* -------- data definitions ----------------------------------------- */ | ||
141 | |||
142 | static capidrv_data global; | ||
143 | static DEFINE_SPINLOCK(global_lock); | ||
144 | |||
145 | static void handle_dtrace_data(capidrv_contr *card, | ||
146 | int send, int level2, u8 *data, u16 len); | ||
147 | |||
148 | /* -------- convert functions ---------------------------------------- */ | ||
149 | |||
150 | static inline u32 b1prot(int l2, int l3) | ||
151 | { | ||
152 | switch (l2) { | ||
153 | case ISDN_PROTO_L2_X75I: | ||
154 | case ISDN_PROTO_L2_X75UI: | ||
155 | case ISDN_PROTO_L2_X75BUI: | ||
156 | return 0; | ||
157 | case ISDN_PROTO_L2_HDLC: | ||
158 | default: | ||
159 | return 0; | ||
160 | case ISDN_PROTO_L2_TRANS: | ||
161 | return 1; | ||
162 | case ISDN_PROTO_L2_V11096: | ||
163 | case ISDN_PROTO_L2_V11019: | ||
164 | case ISDN_PROTO_L2_V11038: | ||
165 | return 2; | ||
166 | case ISDN_PROTO_L2_FAX: | ||
167 | return 4; | ||
168 | case ISDN_PROTO_L2_MODEM: | ||
169 | return 8; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | static inline u32 b2prot(int l2, int l3) | ||
174 | { | ||
175 | switch (l2) { | ||
176 | case ISDN_PROTO_L2_X75I: | ||
177 | case ISDN_PROTO_L2_X75UI: | ||
178 | case ISDN_PROTO_L2_X75BUI: | ||
179 | default: | ||
180 | return 0; | ||
181 | case ISDN_PROTO_L2_HDLC: | ||
182 | case ISDN_PROTO_L2_TRANS: | ||
183 | case ISDN_PROTO_L2_V11096: | ||
184 | case ISDN_PROTO_L2_V11019: | ||
185 | case ISDN_PROTO_L2_V11038: | ||
186 | case ISDN_PROTO_L2_MODEM: | ||
187 | return 1; | ||
188 | case ISDN_PROTO_L2_FAX: | ||
189 | return 4; | ||
190 | } | ||
191 | } | ||
192 | |||
193 | static inline u32 b3prot(int l2, int l3) | ||
194 | { | ||
195 | switch (l2) { | ||
196 | case ISDN_PROTO_L2_X75I: | ||
197 | case ISDN_PROTO_L2_X75UI: | ||
198 | case ISDN_PROTO_L2_X75BUI: | ||
199 | case ISDN_PROTO_L2_HDLC: | ||
200 | case ISDN_PROTO_L2_TRANS: | ||
201 | case ISDN_PROTO_L2_V11096: | ||
202 | case ISDN_PROTO_L2_V11019: | ||
203 | case ISDN_PROTO_L2_V11038: | ||
204 | case ISDN_PROTO_L2_MODEM: | ||
205 | default: | ||
206 | return 0; | ||
207 | case ISDN_PROTO_L2_FAX: | ||
208 | return 4; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | static _cstruct b1config_async_v110(u16 rate) | ||
213 | { | ||
214 | /* CAPI-Spec "B1 Configuration" */ | ||
215 | static unsigned char buf[9]; | ||
216 | buf[0] = 8; /* len */ | ||
217 | /* maximum bitrate */ | ||
218 | buf[1] = rate & 0xff; buf[2] = (rate >> 8) & 0xff; | ||
219 | buf[3] = 8; buf[4] = 0; /* 8 bits per character */ | ||
220 | buf[5] = 0; buf[6] = 0; /* parity none */ | ||
221 | buf[7] = 0; buf[8] = 0; /* 1 stop bit */ | ||
222 | return buf; | ||
223 | } | ||
224 | |||
225 | static _cstruct b1config(int l2, int l3) | ||
226 | { | ||
227 | switch (l2) { | ||
228 | case ISDN_PROTO_L2_X75I: | ||
229 | case ISDN_PROTO_L2_X75UI: | ||
230 | case ISDN_PROTO_L2_X75BUI: | ||
231 | case ISDN_PROTO_L2_HDLC: | ||
232 | case ISDN_PROTO_L2_TRANS: | ||
233 | default: | ||
234 | return NULL; | ||
235 | case ISDN_PROTO_L2_V11096: | ||
236 | return b1config_async_v110(9600); | ||
237 | case ISDN_PROTO_L2_V11019: | ||
238 | return b1config_async_v110(19200); | ||
239 | case ISDN_PROTO_L2_V11038: | ||
240 | return b1config_async_v110(38400); | ||
241 | } | ||
242 | } | ||
243 | |||
244 | static inline u16 si2cip(u8 si1, u8 si2) | ||
245 | { | ||
246 | static const u8 cip[17][5] = | ||
247 | { | ||
248 | /* 0 1 2 3 4 */ | ||
249 | {0, 0, 0, 0, 0}, /*0 */ | ||
250 | {16, 16, 4, 26, 16}, /*1 */ | ||
251 | {17, 17, 17, 4, 4}, /*2 */ | ||
252 | {2, 2, 2, 2, 2}, /*3 */ | ||
253 | {18, 18, 18, 18, 18}, /*4 */ | ||
254 | {2, 2, 2, 2, 2}, /*5 */ | ||
255 | {0, 0, 0, 0, 0}, /*6 */ | ||
256 | {2, 2, 2, 2, 2}, /*7 */ | ||
257 | {2, 2, 2, 2, 2}, /*8 */ | ||
258 | {21, 21, 21, 21, 21}, /*9 */ | ||
259 | {19, 19, 19, 19, 19}, /*10 */ | ||
260 | {0, 0, 0, 0, 0}, /*11 */ | ||
261 | {0, 0, 0, 0, 0}, /*12 */ | ||
262 | {0, 0, 0, 0, 0}, /*13 */ | ||
263 | {0, 0, 0, 0, 0}, /*14 */ | ||
264 | {22, 22, 22, 22, 22}, /*15 */ | ||
265 | {27, 27, 27, 28, 27} /*16 */ | ||
266 | }; | ||
267 | if (si1 > 16) | ||
268 | si1 = 0; | ||
269 | if (si2 > 4) | ||
270 | si2 = 0; | ||
271 | |||
272 | return (u16) cip[si1][si2]; | ||
273 | } | ||
274 | |||
275 | static inline u8 cip2si1(u16 cipval) | ||
276 | { | ||
277 | static const u8 si[32] = | ||
278 | {7, 1, 7, 7, 1, 1, 7, 7, /*0-7 */ | ||
279 | 7, 1, 0, 0, 0, 0, 0, 0, /*8-15 */ | ||
280 | 1, 2, 4, 10, 9, 9, 15, 7, /*16-23 */ | ||
281 | 7, 7, 1, 16, 16, 0, 0, 0}; /*24-31 */ | ||
282 | |||
283 | if (cipval > 31) | ||
284 | cipval = 0; /* .... */ | ||
285 | return si[cipval]; | ||
286 | } | ||
287 | |||
288 | static inline u8 cip2si2(u16 cipval) | ||
289 | { | ||
290 | static const u8 si[32] = | ||
291 | {0, 0, 0, 0, 2, 3, 0, 0, /*0-7 */ | ||
292 | 0, 3, 0, 0, 0, 0, 0, 0, /*8-15 */ | ||
293 | 1, 2, 0, 0, 9, 0, 0, 0, /*16-23 */ | ||
294 | 0, 0, 3, 2, 3, 0, 0, 0}; /*24-31 */ | ||
295 | |||
296 | if (cipval > 31) | ||
297 | cipval = 0; /* .... */ | ||
298 | return si[cipval]; | ||
299 | } | ||
300 | |||
301 | |||
302 | /* -------- controller management ------------------------------------- */ | ||
303 | |||
304 | static inline capidrv_contr *findcontrbydriverid(int driverid) | ||
305 | { | ||
306 | unsigned long flags; | ||
307 | capidrv_contr *p; | ||
308 | |||
309 | spin_lock_irqsave(&global_lock, flags); | ||
310 | for (p = global.contr_list; p; p = p->next) | ||
311 | if (p->myid == driverid) | ||
312 | break; | ||
313 | spin_unlock_irqrestore(&global_lock, flags); | ||
314 | return p; | ||
315 | } | ||
316 | |||
317 | static capidrv_contr *findcontrbynumber(u32 contr) | ||
318 | { | ||
319 | unsigned long flags; | ||
320 | capidrv_contr *p = global.contr_list; | ||
321 | |||
322 | spin_lock_irqsave(&global_lock, flags); | ||
323 | for (p = global.contr_list; p; p = p->next) | ||
324 | if (p->contrnr == contr) | ||
325 | break; | ||
326 | spin_unlock_irqrestore(&global_lock, flags); | ||
327 | return p; | ||
328 | } | ||
329 | |||
330 | |||
331 | /* -------- plci management ------------------------------------------ */ | ||
332 | |||
333 | static capidrv_plci *new_plci(capidrv_contr *card, int chan) | ||
334 | { | ||
335 | capidrv_plci *plcip; | ||
336 | |||
337 | plcip = kzalloc(sizeof(capidrv_plci), GFP_ATOMIC); | ||
338 | |||
339 | if (plcip == NULL) | ||
340 | return NULL; | ||
341 | |||
342 | plcip->state = ST_PLCI_NONE; | ||
343 | plcip->plci = 0; | ||
344 | plcip->msgid = 0; | ||
345 | plcip->chan = chan; | ||
346 | plcip->next = card->plci_list; | ||
347 | card->plci_list = plcip; | ||
348 | card->bchans[chan].plcip = plcip; | ||
349 | |||
350 | return plcip; | ||
351 | } | ||
352 | |||
353 | static capidrv_plci *find_plci_by_plci(capidrv_contr *card, u32 plci) | ||
354 | { | ||
355 | capidrv_plci *p; | ||
356 | for (p = card->plci_list; p; p = p->next) | ||
357 | if (p->plci == plci) | ||
358 | return p; | ||
359 | return NULL; | ||
360 | } | ||
361 | |||
362 | static capidrv_plci *find_plci_by_msgid(capidrv_contr *card, u16 msgid) | ||
363 | { | ||
364 | capidrv_plci *p; | ||
365 | for (p = card->plci_list; p; p = p->next) | ||
366 | if (p->msgid == msgid) | ||
367 | return p; | ||
368 | return NULL; | ||
369 | } | ||
370 | |||
371 | static capidrv_plci *find_plci_by_ncci(capidrv_contr *card, u32 ncci) | ||
372 | { | ||
373 | capidrv_plci *p; | ||
374 | for (p = card->plci_list; p; p = p->next) | ||
375 | if (p->plci == (ncci & 0xffff)) | ||
376 | return p; | ||
377 | return NULL; | ||
378 | } | ||
379 | |||
380 | static void free_plci(capidrv_contr *card, capidrv_plci *plcip) | ||
381 | { | ||
382 | capidrv_plci **pp; | ||
383 | |||
384 | for (pp = &card->plci_list; *pp; pp = &(*pp)->next) { | ||
385 | if (*pp == plcip) { | ||
386 | *pp = (*pp)->next; | ||
387 | card->bchans[plcip->chan].plcip = NULL; | ||
388 | card->bchans[plcip->chan].disconnecting = 0; | ||
389 | card->bchans[plcip->chan].incoming = 0; | ||
390 | kfree(plcip); | ||
391 | return; | ||
392 | } | ||
393 | } | ||
394 | printk(KERN_ERR "capidrv-%d: free_plci %p (0x%x) not found, Huh?\n", | ||
395 | card->contrnr, plcip, plcip->plci); | ||
396 | } | ||
397 | |||
398 | /* -------- ncci management ------------------------------------------ */ | ||
399 | |||
400 | static inline capidrv_ncci *new_ncci(capidrv_contr *card, | ||
401 | capidrv_plci *plcip, | ||
402 | u32 ncci) | ||
403 | { | ||
404 | capidrv_ncci *nccip; | ||
405 | |||
406 | nccip = kzalloc(sizeof(capidrv_ncci), GFP_ATOMIC); | ||
407 | |||
408 | if (nccip == NULL) | ||
409 | return NULL; | ||
410 | |||
411 | nccip->ncci = ncci; | ||
412 | nccip->state = ST_NCCI_NONE; | ||
413 | nccip->plcip = plcip; | ||
414 | nccip->chan = plcip->chan; | ||
415 | nccip->datahandle = 0; | ||
416 | |||
417 | nccip->next = plcip->ncci_list; | ||
418 | plcip->ncci_list = nccip; | ||
419 | |||
420 | card->bchans[plcip->chan].nccip = nccip; | ||
421 | |||
422 | return nccip; | ||
423 | } | ||
424 | |||
425 | static inline capidrv_ncci *find_ncci(capidrv_contr *card, u32 ncci) | ||
426 | { | ||
427 | capidrv_plci *plcip; | ||
428 | capidrv_ncci *p; | ||
429 | |||
430 | if ((plcip = find_plci_by_ncci(card, ncci)) == NULL) | ||
431 | return NULL; | ||
432 | |||
433 | for (p = plcip->ncci_list; p; p = p->next) | ||
434 | if (p->ncci == ncci) | ||
435 | return p; | ||
436 | return NULL; | ||
437 | } | ||
438 | |||
439 | static inline capidrv_ncci *find_ncci_by_msgid(capidrv_contr *card, | ||
440 | u32 ncci, u16 msgid) | ||
441 | { | ||
442 | capidrv_plci *plcip; | ||
443 | capidrv_ncci *p; | ||
444 | |||
445 | if ((plcip = find_plci_by_ncci(card, ncci)) == NULL) | ||
446 | return NULL; | ||
447 | |||
448 | for (p = plcip->ncci_list; p; p = p->next) | ||
449 | if (p->msgid == msgid) | ||
450 | return p; | ||
451 | return NULL; | ||
452 | } | ||
453 | |||
454 | static void free_ncci(capidrv_contr *card, struct capidrv_ncci *nccip) | ||
455 | { | ||
456 | struct capidrv_ncci **pp; | ||
457 | |||
458 | for (pp = &(nccip->plcip->ncci_list); *pp; pp = &(*pp)->next) { | ||
459 | if (*pp == nccip) { | ||
460 | *pp = (*pp)->next; | ||
461 | break; | ||
462 | } | ||
463 | } | ||
464 | card->bchans[nccip->chan].nccip = NULL; | ||
465 | kfree(nccip); | ||
466 | } | ||
467 | |||
468 | static int capidrv_add_ack(struct capidrv_ncci *nccip, | ||
469 | u16 datahandle, int len) | ||
470 | { | ||
471 | struct ncci_datahandle_queue *n, **pp; | ||
472 | |||
473 | n = kmalloc(sizeof(struct ncci_datahandle_queue), GFP_ATOMIC); | ||
474 | if (!n) { | ||
475 | printk(KERN_ERR "capidrv: kmalloc ncci_datahandle failed\n"); | ||
476 | return -1; | ||
477 | } | ||
478 | n->next = NULL; | ||
479 | n->datahandle = datahandle; | ||
480 | n->len = len; | ||
481 | for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next); | ||
482 | *pp = n; | ||
483 | return 0; | ||
484 | } | ||
485 | |||
486 | static int capidrv_del_ack(struct capidrv_ncci *nccip, u16 datahandle) | ||
487 | { | ||
488 | struct ncci_datahandle_queue **pp, *p; | ||
489 | int len; | ||
490 | |||
491 | for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) { | ||
492 | if ((*pp)->datahandle == datahandle) { | ||
493 | p = *pp; | ||
494 | len = p->len; | ||
495 | *pp = (*pp)->next; | ||
496 | kfree(p); | ||
497 | return len; | ||
498 | } | ||
499 | } | ||
500 | return -1; | ||
501 | } | ||
502 | |||
503 | /* -------- convert and send capi message ---------------------------- */ | ||
504 | |||
505 | static void send_message(capidrv_contr *card, _cmsg *cmsg) | ||
506 | { | ||
507 | struct sk_buff *skb; | ||
508 | size_t len; | ||
509 | |||
510 | if (capi_cmsg2message(cmsg, cmsg->buf)) { | ||
511 | printk(KERN_ERR "capidrv::send_message: parser failure\n"); | ||
512 | return; | ||
513 | } | ||
514 | len = CAPIMSG_LEN(cmsg->buf); | ||
515 | skb = alloc_skb(len, GFP_ATOMIC); | ||
516 | if (!skb) { | ||
517 | printk(KERN_ERR "capidrv::send_message: can't allocate mem\n"); | ||
518 | return; | ||
519 | } | ||
520 | skb_put_data(skb, cmsg->buf, len); | ||
521 | if (capi20_put_message(&global.ap, skb) != CAPI_NOERROR) | ||
522 | kfree_skb(skb); | ||
523 | } | ||
524 | |||
525 | /* -------- state machine -------------------------------------------- */ | ||
526 | |||
527 | struct listenstatechange { | ||
528 | int actstate; | ||
529 | int nextstate; | ||
530 | int event; | ||
531 | }; | ||
532 | |||
533 | static struct listenstatechange listentable[] = | ||
534 | { | ||
535 | {ST_LISTEN_NONE, ST_LISTEN_WAIT_CONF, EV_LISTEN_REQ}, | ||
536 | {ST_LISTEN_ACTIVE, ST_LISTEN_ACTIVE_WAIT_CONF, EV_LISTEN_REQ}, | ||
537 | {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_ERROR}, | ||
538 | {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_ERROR}, | ||
539 | {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY}, | ||
540 | {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY}, | ||
541 | {ST_LISTEN_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK}, | ||
542 | {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK}, | ||
543 | {}, | ||
544 | }; | ||
545 | |||
546 | static void listen_change_state(capidrv_contr *card, int event) | ||
547 | { | ||
548 | struct listenstatechange *p = listentable; | ||
549 | while (p->event) { | ||
550 | if (card->state == p->actstate && p->event == event) { | ||
551 | if (debugmode) | ||
552 | printk(KERN_DEBUG "capidrv-%d: listen_change_state %d -> %d\n", | ||
553 | card->contrnr, card->state, p->nextstate); | ||
554 | card->state = p->nextstate; | ||
555 | return; | ||
556 | } | ||
557 | p++; | ||
558 | } | ||
559 | printk(KERN_ERR "capidrv-%d: listen_change_state state=%d event=%d ????\n", | ||
560 | card->contrnr, card->state, event); | ||
561 | |||
562 | } | ||
563 | |||
564 | /* ------------------------------------------------------------------ */ | ||
565 | |||
566 | static void p0(capidrv_contr *card, capidrv_plci *plci) | ||
567 | { | ||
568 | isdn_ctrl cmd; | ||
569 | |||
570 | card->bchans[plci->chan].contr = NULL; | ||
571 | cmd.command = ISDN_STAT_DHUP; | ||
572 | cmd.driver = card->myid; | ||
573 | cmd.arg = plci->chan; | ||
574 | card->interface.statcallb(&cmd); | ||
575 | free_plci(card, plci); | ||
576 | } | ||
577 | |||
578 | /* ------------------------------------------------------------------ */ | ||
579 | |||
580 | struct plcistatechange { | ||
581 | int actstate; | ||
582 | int nextstate; | ||
583 | int event; | ||
584 | void (*changefunc)(capidrv_contr *card, capidrv_plci *plci); | ||
585 | }; | ||
586 | |||
587 | static struct plcistatechange plcitable[] = | ||
588 | { | ||
589 | /* P-0 */ | ||
590 | {ST_PLCI_NONE, ST_PLCI_OUTGOING, EV_PLCI_CONNECT_REQ, NULL}, | ||
591 | {ST_PLCI_NONE, ST_PLCI_ALLOCATED, EV_PLCI_FACILITY_IND_UP, NULL}, | ||
592 | {ST_PLCI_NONE, ST_PLCI_INCOMING, EV_PLCI_CONNECT_IND, NULL}, | ||
593 | {ST_PLCI_NONE, ST_PLCI_RESUMEING, EV_PLCI_RESUME_REQ, NULL}, | ||
594 | /* P-0.1 */ | ||
595 | {ST_PLCI_OUTGOING, ST_PLCI_NONE, EV_PLCI_CONNECT_CONF_ERROR, p0}, | ||
596 | {ST_PLCI_OUTGOING, ST_PLCI_ALLOCATED, EV_PLCI_CONNECT_CONF_OK, NULL}, | ||
597 | /* P-1 */ | ||
598 | {ST_PLCI_ALLOCATED, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, NULL}, | ||
599 | {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL}, | ||
600 | {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL}, | ||
601 | {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL}, | ||
602 | /* P-ACT */ | ||
603 | {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL}, | ||
604 | {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL}, | ||
605 | {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL}, | ||
606 | {ST_PLCI_ACTIVE, ST_PLCI_HELD, EV_PLCI_HOLD_IND, NULL}, | ||
607 | {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_SUSPEND_IND, NULL}, | ||
608 | /* P-2 */ | ||
609 | {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, NULL}, | ||
610 | {ST_PLCI_INCOMING, ST_PLCI_FACILITY_IND, EV_PLCI_FACILITY_IND_UP, NULL}, | ||
611 | {ST_PLCI_INCOMING, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_RESP, NULL}, | ||
612 | {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL}, | ||
613 | {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL}, | ||
614 | {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL}, | ||
615 | {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CD_IND, NULL}, | ||
616 | /* P-3 */ | ||
617 | {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, NULL}, | ||
618 | {ST_PLCI_FACILITY_IND, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_ACTIVE_IND, NULL}, | ||
619 | {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL}, | ||
620 | {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL}, | ||
621 | {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL}, | ||
622 | /* P-4 */ | ||
623 | {ST_PLCI_ACCEPTING, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, NULL}, | ||
624 | {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL}, | ||
625 | {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL}, | ||
626 | {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL}, | ||
627 | /* P-5 */ | ||
628 | {ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL}, | ||
629 | /* P-6 */ | ||
630 | {ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0}, | ||
631 | /* P-0.Res */ | ||
632 | {ST_PLCI_RESUMEING, ST_PLCI_NONE, EV_PLCI_RESUME_CONF_ERROR, p0}, | ||
633 | {ST_PLCI_RESUMEING, ST_PLCI_RESUME, EV_PLCI_RESUME_CONF_OK, NULL}, | ||
634 | /* P-RES */ | ||
635 | {ST_PLCI_RESUME, ST_PLCI_ACTIVE, EV_PLCI_RESUME_IND, NULL}, | ||
636 | /* P-HELD */ | ||
637 | {ST_PLCI_HELD, ST_PLCI_ACTIVE, EV_PLCI_RETRIEVE_IND, NULL}, | ||
638 | {}, | ||
639 | }; | ||
640 | |||
641 | static void plci_change_state(capidrv_contr *card, capidrv_plci *plci, int event) | ||
642 | { | ||
643 | struct plcistatechange *p = plcitable; | ||
644 | while (p->event) { | ||
645 | if (plci->state == p->actstate && p->event == event) { | ||
646 | if (debugmode) | ||
647 | printk(KERN_DEBUG "capidrv-%d: plci_change_state:0x%x %d -> %d\n", | ||
648 | card->contrnr, plci->plci, plci->state, p->nextstate); | ||
649 | plci->state = p->nextstate; | ||
650 | if (p->changefunc) | ||
651 | p->changefunc(card, plci); | ||
652 | return; | ||
653 | } | ||
654 | p++; | ||
655 | } | ||
656 | printk(KERN_ERR "capidrv-%d: plci_change_state:0x%x state=%d event=%d ????\n", | ||
657 | card->contrnr, plci->plci, plci->state, event); | ||
658 | } | ||
659 | |||
660 | /* ------------------------------------------------------------------ */ | ||
661 | |||
662 | static _cmsg cmsg; | ||
663 | |||
664 | static void n0(capidrv_contr *card, capidrv_ncci *ncci) | ||
665 | { | ||
666 | isdn_ctrl cmd; | ||
667 | |||
668 | capi_fill_DISCONNECT_REQ(&cmsg, | ||
669 | global.ap.applid, | ||
670 | card->msgid++, | ||
671 | ncci->plcip->plci, | ||
672 | NULL, /* BChannelinformation */ | ||
673 | NULL, /* Keypadfacility */ | ||
674 | NULL, /* Useruserdata */ /* $$$$ */ | ||
675 | NULL /* Facilitydataarray */ | ||
676 | ); | ||
677 | plci_change_state(card, ncci->plcip, EV_PLCI_DISCONNECT_REQ); | ||
678 | send_message(card, &cmsg); | ||
679 | |||
680 | cmd.command = ISDN_STAT_BHUP; | ||
681 | cmd.driver = card->myid; | ||
682 | cmd.arg = ncci->chan; | ||
683 | card->interface.statcallb(&cmd); | ||
684 | free_ncci(card, ncci); | ||
685 | } | ||
686 | |||
687 | /* ------------------------------------------------------------------ */ | ||
688 | |||
689 | struct nccistatechange { | ||
690 | int actstate; | ||
691 | int nextstate; | ||
692 | int event; | ||
693 | void (*changefunc)(capidrv_contr *card, capidrv_ncci *ncci); | ||
694 | }; | ||
695 | |||
696 | static struct nccistatechange nccitable[] = | ||
697 | { | ||
698 | /* N-0 */ | ||
699 | {ST_NCCI_NONE, ST_NCCI_OUTGOING, EV_NCCI_CONNECT_B3_REQ, NULL}, | ||
700 | {ST_NCCI_NONE, ST_NCCI_INCOMING, EV_NCCI_CONNECT_B3_IND, NULL}, | ||
701 | /* N-0.1 */ | ||
702 | {ST_NCCI_OUTGOING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_CONF_OK, NULL}, | ||
703 | {ST_NCCI_OUTGOING, ST_NCCI_NONE, EV_NCCI_CONNECT_B3_CONF_ERROR, n0}, | ||
704 | /* N-1 */ | ||
705 | {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_CONNECT_B3_REJECT, NULL}, | ||
706 | {ST_NCCI_INCOMING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_RESP, NULL}, | ||
707 | {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL}, | ||
708 | {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL}, | ||
709 | /* N-2 */ | ||
710 | {ST_NCCI_ALLOCATED, ST_NCCI_ACTIVE, EV_NCCI_CONNECT_B3_ACTIVE_IND, NULL}, | ||
711 | {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL}, | ||
712 | {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL}, | ||
713 | /* N-ACT */ | ||
714 | {ST_NCCI_ACTIVE, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, NULL}, | ||
715 | {ST_NCCI_ACTIVE, ST_NCCI_RESETING, EV_NCCI_RESET_B3_REQ, NULL}, | ||
716 | {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL}, | ||
717 | {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL}, | ||
718 | /* N-3 */ | ||
719 | {ST_NCCI_RESETING, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, NULL}, | ||
720 | {ST_NCCI_RESETING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL}, | ||
721 | {ST_NCCI_RESETING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL}, | ||
722 | /* N-4 */ | ||
723 | {ST_NCCI_DISCONNECTING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL}, | ||
724 | {ST_NCCI_DISCONNECTING, ST_NCCI_PREVIOUS, EV_NCCI_DISCONNECT_B3_CONF_ERROR, NULL}, | ||
725 | /* N-5 */ | ||
726 | {ST_NCCI_DISCONNECTED, ST_NCCI_NONE, EV_NCCI_DISCONNECT_B3_RESP, n0}, | ||
727 | {}, | ||
728 | }; | ||
729 | |||
730 | static void ncci_change_state(capidrv_contr *card, capidrv_ncci *ncci, int event) | ||
731 | { | ||
732 | struct nccistatechange *p = nccitable; | ||
733 | while (p->event) { | ||
734 | if (ncci->state == p->actstate && p->event == event) { | ||
735 | if (debugmode) | ||
736 | printk(KERN_DEBUG "capidrv-%d: ncci_change_state:0x%x %d -> %d\n", | ||
737 | card->contrnr, ncci->ncci, ncci->state, p->nextstate); | ||
738 | if (p->nextstate == ST_NCCI_PREVIOUS) { | ||
739 | ncci->state = ncci->oldstate; | ||
740 | ncci->oldstate = p->actstate; | ||
741 | } else { | ||
742 | ncci->oldstate = p->actstate; | ||
743 | ncci->state = p->nextstate; | ||
744 | } | ||
745 | if (p->changefunc) | ||
746 | p->changefunc(card, ncci); | ||
747 | return; | ||
748 | } | ||
749 | p++; | ||
750 | } | ||
751 | printk(KERN_ERR "capidrv-%d: ncci_change_state:0x%x state=%d event=%d ????\n", | ||
752 | card->contrnr, ncci->ncci, ncci->state, event); | ||
753 | } | ||
754 | |||
755 | /* ------------------------------------------------------------------- */ | ||
756 | |||
757 | static inline int new_bchan(capidrv_contr *card) | ||
758 | { | ||
759 | int i; | ||
760 | for (i = 0; i < card->nbchan; i++) { | ||
761 | if (card->bchans[i].plcip == NULL) { | ||
762 | card->bchans[i].disconnecting = 0; | ||
763 | return i; | ||
764 | } | ||
765 | } | ||
766 | return -1; | ||
767 | } | ||
768 | |||
769 | /* ------------------------------------------------------------------- */ | ||
770 | static char *capi_info2str(u16 reason) | ||
771 | { | ||
772 | #ifndef CONFIG_ISDN_CAPI_CAPIDRV_VERBOSE | ||
773 | return ".."; | ||
774 | #else | ||
775 | switch (reason) { | ||
776 | |||
777 | /*-- informative values (corresponding message was processed) -----*/ | ||
778 | case 0x0001: | ||
779 | return "NCPI not supported by current protocol, NCPI ignored"; | ||
780 | case 0x0002: | ||
781 | return "Flags not supported by current protocol, flags ignored"; | ||
782 | case 0x0003: | ||
783 | return "Alert already sent by another application"; | ||
784 | |||
785 | /*-- error information concerning CAPI_REGISTER -----*/ | ||
786 | case 0x1001: | ||
787 | return "Too many applications"; | ||
788 | case 0x1002: | ||
789 | return "Logical block size too small, must be at least 128 Bytes"; | ||
790 | case 0x1003: | ||
791 | return "Buffer exceeds 64 kByte"; | ||
792 | case 0x1004: | ||
793 | return "Message buffer size too small, must be at least 1024 Bytes"; | ||
794 | case 0x1005: | ||
795 | return "Max. number of logical connections not supported"; | ||
796 | case 0x1006: | ||
797 | return "Reserved"; | ||
798 | case 0x1007: | ||
799 | return "The message could not be accepted because of an internal busy condition"; | ||
800 | case 0x1008: | ||
801 | return "OS resource error (no memory ?)"; | ||
802 | case 0x1009: | ||
803 | return "CAPI not installed"; | ||
804 | case 0x100A: | ||
805 | return "Controller does not support external equipment"; | ||
806 | case 0x100B: | ||
807 | return "Controller does only support external equipment"; | ||
808 | |||
809 | /*-- error information concerning message exchange functions -----*/ | ||
810 | case 0x1101: | ||
811 | return "Illegal application number"; | ||
812 | case 0x1102: | ||
813 | return "Illegal command or subcommand or message length less than 12 bytes"; | ||
814 | case 0x1103: | ||
815 | return "The message could not be accepted because of a queue full condition !! The error code does not imply that CAPI cannot receive messages directed to another controller, PLCI or NCCI"; | ||
816 | case 0x1104: | ||
817 | return "Queue is empty"; | ||
818 | case 0x1105: | ||
819 | return "Queue overflow, a message was lost !! This indicates a configuration error. The only recovery from this error is to perform a CAPI_RELEASE"; | ||
820 | case 0x1106: | ||
821 | return "Unknown notification parameter"; | ||
822 | case 0x1107: | ||
823 | return "The Message could not be accepted because of an internal busy condition"; | ||
824 | case 0x1108: | ||
825 | return "OS Resource error (no memory ?)"; | ||
826 | case 0x1109: | ||
827 | return "CAPI not installed"; | ||
828 | case 0x110A: | ||
829 | return "Controller does not support external equipment"; | ||
830 | case 0x110B: | ||
831 | return "Controller does only support external equipment"; | ||
832 | |||
833 | /*-- error information concerning resource / coding problems -----*/ | ||
834 | case 0x2001: | ||
835 | return "Message not supported in current state"; | ||
836 | case 0x2002: | ||
837 | return "Illegal Controller / PLCI / NCCI"; | ||
838 | case 0x2003: | ||
839 | return "Out of PLCI"; | ||
840 | case 0x2004: | ||
841 | return "Out of NCCI"; | ||
842 | case 0x2005: | ||
843 | return "Out of LISTEN"; | ||
844 | case 0x2006: | ||
845 | return "Out of FAX resources (protocol T.30)"; | ||
846 | case 0x2007: | ||
847 | return "Illegal message parameter coding"; | ||
848 | |||
849 | /*-- error information concerning requested services -----*/ | ||
850 | case 0x3001: | ||
851 | return "B1 protocol not supported"; | ||
852 | case 0x3002: | ||
853 | return "B2 protocol not supported"; | ||
854 | case 0x3003: | ||
855 | return "B3 protocol not supported"; | ||
856 | case 0x3004: | ||
857 | return "B1 protocol parameter not supported"; | ||
858 | case 0x3005: | ||
859 | return "B2 protocol parameter not supported"; | ||
860 | case 0x3006: | ||
861 | return "B3 protocol parameter not supported"; | ||
862 | case 0x3007: | ||
863 | return "B protocol combination not supported"; | ||
864 | case 0x3008: | ||
865 | return "NCPI not supported"; | ||
866 | case 0x3009: | ||
867 | return "CIP Value unknown"; | ||
868 | case 0x300A: | ||
869 | return "Flags not supported (reserved bits)"; | ||
870 | case 0x300B: | ||
871 | return "Facility not supported"; | ||
872 | case 0x300C: | ||
873 | return "Data length not supported by current protocol"; | ||
874 | case 0x300D: | ||
875 | return "Reset procedure not supported by current protocol"; | ||
876 | |||
877 | /*-- informations about the clearing of a physical connection -----*/ | ||
878 | case 0x3301: | ||
879 | return "Protocol error layer 1 (broken line or B-channel removed by signalling protocol)"; | ||
880 | case 0x3302: | ||
881 | return "Protocol error layer 2"; | ||
882 | case 0x3303: | ||
883 | return "Protocol error layer 3"; | ||
884 | case 0x3304: | ||
885 | return "Another application got that call"; | ||
886 | /*-- T.30 specific reasons -----*/ | ||
887 | case 0x3311: | ||
888 | return "Connecting not successful (remote station is no FAX G3 machine)"; | ||
889 | case 0x3312: | ||
890 | return "Connecting not successful (training error)"; | ||
891 | case 0x3313: | ||
892 | return "Disconnected before transfer (remote station does not support transfer mode, e.g. resolution)"; | ||
893 | case 0x3314: | ||
894 | return "Disconnected during transfer (remote abort)"; | ||
895 | case 0x3315: | ||
896 | return "Disconnected during transfer (remote procedure error, e.g. unsuccessful repetition of T.30 commands)"; | ||
897 | case 0x3316: | ||
898 | return "Disconnected during transfer (local tx data underrun)"; | ||
899 | case 0x3317: | ||
900 | return "Disconnected during transfer (local rx data overflow)"; | ||
901 | case 0x3318: | ||
902 | return "Disconnected during transfer (local abort)"; | ||
903 | case 0x3319: | ||
904 | return "Illegal parameter coding (e.g. SFF coding error)"; | ||
905 | |||
906 | /*-- disconnect causes from the network according to ETS 300 102-1/Q.931 -----*/ | ||
907 | case 0x3481: return "Unallocated (unassigned) number"; | ||
908 | case 0x3482: return "No route to specified transit network"; | ||
909 | case 0x3483: return "No route to destination"; | ||
910 | case 0x3486: return "Channel unacceptable"; | ||
911 | case 0x3487: | ||
912 | return "Call awarded and being delivered in an established channel"; | ||
913 | case 0x3490: return "Normal call clearing"; | ||
914 | case 0x3491: return "User busy"; | ||
915 | case 0x3492: return "No user responding"; | ||
916 | case 0x3493: return "No answer from user (user alerted)"; | ||
917 | case 0x3495: return "Call rejected"; | ||
918 | case 0x3496: return "Number changed"; | ||
919 | case 0x349A: return "Non-selected user clearing"; | ||
920 | case 0x349B: return "Destination out of order"; | ||
921 | case 0x349C: return "Invalid number format"; | ||
922 | case 0x349D: return "Facility rejected"; | ||
923 | case 0x349E: return "Response to STATUS ENQUIRY"; | ||
924 | case 0x349F: return "Normal, unspecified"; | ||
925 | case 0x34A2: return "No circuit / channel available"; | ||
926 | case 0x34A6: return "Network out of order"; | ||
927 | case 0x34A9: return "Temporary failure"; | ||
928 | case 0x34AA: return "Switching equipment congestion"; | ||
929 | case 0x34AB: return "Access information discarded"; | ||
930 | case 0x34AC: return "Requested circuit / channel not available"; | ||
931 | case 0x34AF: return "Resources unavailable, unspecified"; | ||
932 | case 0x34B1: return "Quality of service unavailable"; | ||
933 | case 0x34B2: return "Requested facility not subscribed"; | ||
934 | case 0x34B9: return "Bearer capability not authorized"; | ||
935 | case 0x34BA: return "Bearer capability not presently available"; | ||
936 | case 0x34BF: return "Service or option not available, unspecified"; | ||
937 | case 0x34C1: return "Bearer capability not implemented"; | ||
938 | case 0x34C2: return "Channel type not implemented"; | ||
939 | case 0x34C5: return "Requested facility not implemented"; | ||
940 | case 0x34C6: return "Only restricted digital information bearer capability is available"; | ||
941 | case 0x34CF: return "Service or option not implemented, unspecified"; | ||
942 | case 0x34D1: return "Invalid call reference value"; | ||
943 | case 0x34D2: return "Identified channel does not exist"; | ||
944 | case 0x34D3: return "A suspended call exists, but this call identity does not"; | ||
945 | case 0x34D4: return "Call identity in use"; | ||
946 | case 0x34D5: return "No call suspended"; | ||
947 | case 0x34D6: return "Call having the requested call identity has been cleared"; | ||
948 | case 0x34D8: return "Incompatible destination"; | ||
949 | case 0x34DB: return "Invalid transit network selection"; | ||
950 | case 0x34DF: return "Invalid message, unspecified"; | ||
951 | case 0x34E0: return "Mandatory information element is missing"; | ||
952 | case 0x34E1: return "Message type non-existent or not implemented"; | ||
953 | case 0x34E2: return "Message not compatible with call state or message type non-existent or not implemented"; | ||
954 | case 0x34E3: return "Information element non-existent or not implemented"; | ||
955 | case 0x34E4: return "Invalid information element contents"; | ||
956 | case 0x34E5: return "Message not compatible with call state"; | ||
957 | case 0x34E6: return "Recovery on timer expiry"; | ||
958 | case 0x34EF: return "Protocol error, unspecified"; | ||
959 | case 0x34FF: return "Interworking, unspecified"; | ||
960 | |||
961 | default: return "No additional information"; | ||
962 | } | ||
963 | #endif | ||
964 | } | ||
965 | |||
966 | static void handle_controller(_cmsg *cmsg) | ||
967 | { | ||
968 | capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f); | ||
969 | |||
970 | if (!card) { | ||
971 | printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n", | ||
972 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
973 | cmsg->adr.adrController & 0x7f); | ||
974 | return; | ||
975 | } | ||
976 | switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) { | ||
977 | |||
978 | case CAPI_LISTEN_CONF: /* Controller */ | ||
979 | if (debugmode) | ||
980 | printk(KERN_DEBUG "capidrv-%d: listenconf Info=0x%4x (%s) cipmask=0x%x\n", | ||
981 | card->contrnr, cmsg->Info, capi_info2str(cmsg->Info), card->cipmask); | ||
982 | if (cmsg->Info) { | ||
983 | listen_change_state(card, EV_LISTEN_CONF_ERROR); | ||
984 | } else if (card->cipmask == 0) { | ||
985 | listen_change_state(card, EV_LISTEN_CONF_EMPTY); | ||
986 | } else { | ||
987 | listen_change_state(card, EV_LISTEN_CONF_OK); | ||
988 | } | ||
989 | break; | ||
990 | |||
991 | case CAPI_MANUFACTURER_IND: /* Controller */ | ||
992 | if (cmsg->ManuID == 0x214D5641 | ||
993 | && cmsg->Class == 0 | ||
994 | && cmsg->Function == 1) { | ||
995 | u8 *data = cmsg->ManuData + 3; | ||
996 | u16 len = cmsg->ManuData[0]; | ||
997 | u16 layer; | ||
998 | int direction; | ||
999 | if (len == 255) { | ||
1000 | len = (cmsg->ManuData[1] | (cmsg->ManuData[2] << 8)); | ||
1001 | data += 2; | ||
1002 | } | ||
1003 | len -= 2; | ||
1004 | layer = ((*(data - 1)) << 8) | *(data - 2); | ||
1005 | if (layer & 0x300) | ||
1006 | direction = (layer & 0x200) ? 0 : 1; | ||
1007 | else direction = (layer & 0x800) ? 0 : 1; | ||
1008 | if (layer & 0x0C00) { | ||
1009 | if ((layer & 0xff) == 0x80) { | ||
1010 | handle_dtrace_data(card, direction, 1, data, len); | ||
1011 | break; | ||
1012 | } | ||
1013 | } else if ((layer & 0xff) < 0x80) { | ||
1014 | handle_dtrace_data(card, direction, 0, data, len); | ||
1015 | break; | ||
1016 | } | ||
1017 | printk(KERN_INFO "capidrv-%d: %s from controller 0x%x layer 0x%x, ignored\n", | ||
1018 | card->contrnr, | ||
1019 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1020 | cmsg->adr.adrController, layer); | ||
1021 | break; | ||
1022 | } | ||
1023 | goto ignored; | ||
1024 | case CAPI_MANUFACTURER_CONF: /* Controller */ | ||
1025 | if (cmsg->ManuID == 0x214D5641) { | ||
1026 | char *s = NULL; | ||
1027 | switch (cmsg->Class) { | ||
1028 | case 0: break; | ||
1029 | case 1: s = "unknown class"; break; | ||
1030 | case 2: s = "unknown function"; break; | ||
1031 | default: s = "unknown error"; break; | ||
1032 | } | ||
1033 | if (s) | ||
1034 | printk(KERN_INFO "capidrv-%d: %s from controller 0x%x function %d: %s\n", | ||
1035 | card->contrnr, | ||
1036 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1037 | cmsg->adr.adrController, | ||
1038 | cmsg->Function, s); | ||
1039 | break; | ||
1040 | } | ||
1041 | goto ignored; | ||
1042 | case CAPI_FACILITY_IND: /* Controller/plci/ncci */ | ||
1043 | goto ignored; | ||
1044 | case CAPI_FACILITY_CONF: /* Controller/plci/ncci */ | ||
1045 | goto ignored; | ||
1046 | case CAPI_INFO_IND: /* Controller/plci */ | ||
1047 | goto ignored; | ||
1048 | case CAPI_INFO_CONF: /* Controller/plci */ | ||
1049 | goto ignored; | ||
1050 | |||
1051 | default: | ||
1052 | printk(KERN_ERR "capidrv-%d: got %s from controller 0x%x ???", | ||
1053 | card->contrnr, | ||
1054 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1055 | cmsg->adr.adrController); | ||
1056 | } | ||
1057 | return; | ||
1058 | |||
1059 | ignored: | ||
1060 | printk(KERN_INFO "capidrv-%d: %s from controller 0x%x ignored\n", | ||
1061 | card->contrnr, | ||
1062 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1063 | cmsg->adr.adrController); | ||
1064 | } | ||
1065 | |||
1066 | static void handle_incoming_call(capidrv_contr *card, _cmsg *cmsg) | ||
1067 | { | ||
1068 | capidrv_plci *plcip; | ||
1069 | capidrv_bchan *bchan; | ||
1070 | isdn_ctrl cmd; | ||
1071 | int chan; | ||
1072 | |||
1073 | if ((chan = new_bchan(card)) == -1) { | ||
1074 | printk(KERN_ERR "capidrv-%d: incoming call on not existing bchan ?\n", card->contrnr); | ||
1075 | return; | ||
1076 | } | ||
1077 | bchan = &card->bchans[chan]; | ||
1078 | if ((plcip = new_plci(card, chan)) == NULL) { | ||
1079 | printk(KERN_ERR "capidrv-%d: incoming call: no memory, sorry.\n", card->contrnr); | ||
1080 | return; | ||
1081 | } | ||
1082 | bchan->incoming = 1; | ||
1083 | plcip->plci = cmsg->adr.adrPLCI; | ||
1084 | plci_change_state(card, plcip, EV_PLCI_CONNECT_IND); | ||
1085 | |||
1086 | cmd.command = ISDN_STAT_ICALL; | ||
1087 | cmd.driver = card->myid; | ||
1088 | cmd.arg = chan; | ||
1089 | memset(&cmd.parm.setup, 0, sizeof(cmd.parm.setup)); | ||
1090 | strncpy(cmd.parm.setup.phone, | ||
1091 | cmsg->CallingPartyNumber + 3, | ||
1092 | cmsg->CallingPartyNumber[0] - 2); | ||
1093 | strncpy(cmd.parm.setup.eazmsn, | ||
1094 | cmsg->CalledPartyNumber + 2, | ||
1095 | cmsg->CalledPartyNumber[0] - 1); | ||
1096 | cmd.parm.setup.si1 = cip2si1(cmsg->CIPValue); | ||
1097 | cmd.parm.setup.si2 = cip2si2(cmsg->CIPValue); | ||
1098 | cmd.parm.setup.plan = cmsg->CallingPartyNumber[1]; | ||
1099 | cmd.parm.setup.screen = cmsg->CallingPartyNumber[2]; | ||
1100 | |||
1101 | printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s\n", | ||
1102 | card->contrnr, | ||
1103 | cmd.parm.setup.phone, | ||
1104 | cmd.parm.setup.si1, | ||
1105 | cmd.parm.setup.si2, | ||
1106 | cmd.parm.setup.eazmsn); | ||
1107 | |||
1108 | if (cmd.parm.setup.si1 == 1 && cmd.parm.setup.si2 != 0) { | ||
1109 | printk(KERN_INFO "capidrv-%d: patching si2=%d to 0 for VBOX\n", | ||
1110 | card->contrnr, | ||
1111 | cmd.parm.setup.si2); | ||
1112 | cmd.parm.setup.si2 = 0; | ||
1113 | } | ||
1114 | |||
1115 | switch (card->interface.statcallb(&cmd)) { | ||
1116 | case 0: | ||
1117 | case 3: | ||
1118 | /* No device matching this call. | ||
1119 | * and isdn_common.c has send a HANGUP command | ||
1120 | * which is ignored in state ST_PLCI_INCOMING, | ||
1121 | * so we send RESP to ignore the call | ||
1122 | */ | ||
1123 | capi_cmsg_answer(cmsg); | ||
1124 | cmsg->Reject = 1; /* ignore */ | ||
1125 | plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT); | ||
1126 | send_message(card, cmsg); | ||
1127 | printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s ignored\n", | ||
1128 | card->contrnr, | ||
1129 | cmd.parm.setup.phone, | ||
1130 | cmd.parm.setup.si1, | ||
1131 | cmd.parm.setup.si2, | ||
1132 | cmd.parm.setup.eazmsn); | ||
1133 | break; | ||
1134 | case 1: | ||
1135 | /* At least one device matching this call (RING on ttyI) | ||
1136 | * HL-driver may send ALERTING on the D-channel in this | ||
1137 | * case. | ||
1138 | * really means: RING on ttyI or a net interface | ||
1139 | * accepted this call already. | ||
1140 | * | ||
1141 | * If the call was accepted, state has already changed, | ||
1142 | * and CONNECT_RESP already sent. | ||
1143 | */ | ||
1144 | if (plcip->state == ST_PLCI_INCOMING) { | ||
1145 | printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s tty alerting\n", | ||
1146 | card->contrnr, | ||
1147 | cmd.parm.setup.phone, | ||
1148 | cmd.parm.setup.si1, | ||
1149 | cmd.parm.setup.si2, | ||
1150 | cmd.parm.setup.eazmsn); | ||
1151 | capi_fill_ALERT_REQ(cmsg, | ||
1152 | global.ap.applid, | ||
1153 | card->msgid++, | ||
1154 | plcip->plci, /* adr */ | ||
1155 | NULL,/* BChannelinformation */ | ||
1156 | NULL,/* Keypadfacility */ | ||
1157 | NULL,/* Useruserdata */ | ||
1158 | NULL /* Facilitydataarray */ | ||
1159 | ); | ||
1160 | plcip->msgid = cmsg->Messagenumber; | ||
1161 | send_message(card, cmsg); | ||
1162 | } else { | ||
1163 | printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s on netdev\n", | ||
1164 | card->contrnr, | ||
1165 | cmd.parm.setup.phone, | ||
1166 | cmd.parm.setup.si1, | ||
1167 | cmd.parm.setup.si2, | ||
1168 | cmd.parm.setup.eazmsn); | ||
1169 | } | ||
1170 | break; | ||
1171 | |||
1172 | case 2: /* Call will be rejected. */ | ||
1173 | capi_cmsg_answer(cmsg); | ||
1174 | cmsg->Reject = 2; /* reject call, normal call clearing */ | ||
1175 | plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT); | ||
1176 | send_message(card, cmsg); | ||
1177 | break; | ||
1178 | |||
1179 | default: | ||
1180 | /* An error happened. (Invalid parameters for example.) */ | ||
1181 | capi_cmsg_answer(cmsg); | ||
1182 | cmsg->Reject = 8; /* reject call, | ||
1183 | destination out of order */ | ||
1184 | plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT); | ||
1185 | send_message(card, cmsg); | ||
1186 | break; | ||
1187 | } | ||
1188 | return; | ||
1189 | } | ||
1190 | |||
1191 | static void handle_plci(_cmsg *cmsg) | ||
1192 | { | ||
1193 | capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f); | ||
1194 | capidrv_plci *plcip; | ||
1195 | isdn_ctrl cmd; | ||
1196 | _cdebbuf *cdb; | ||
1197 | |||
1198 | if (!card) { | ||
1199 | printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n", | ||
1200 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1201 | cmsg->adr.adrController & 0x7f); | ||
1202 | return; | ||
1203 | } | ||
1204 | switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) { | ||
1205 | |||
1206 | case CAPI_DISCONNECT_IND: /* plci */ | ||
1207 | if (cmsg->Reason) { | ||
1208 | printk(KERN_INFO "capidrv-%d: %s reason 0x%x (%s) for plci 0x%x\n", | ||
1209 | card->contrnr, | ||
1210 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1211 | cmsg->Reason, capi_info2str(cmsg->Reason), cmsg->adr.adrPLCI); | ||
1212 | } | ||
1213 | if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) { | ||
1214 | capi_cmsg_answer(cmsg); | ||
1215 | send_message(card, cmsg); | ||
1216 | goto notfound; | ||
1217 | } | ||
1218 | card->bchans[plcip->chan].disconnecting = 1; | ||
1219 | plci_change_state(card, plcip, EV_PLCI_DISCONNECT_IND); | ||
1220 | capi_cmsg_answer(cmsg); | ||
1221 | plci_change_state(card, plcip, EV_PLCI_DISCONNECT_RESP); | ||
1222 | send_message(card, cmsg); | ||
1223 | break; | ||
1224 | |||
1225 | case CAPI_DISCONNECT_CONF: /* plci */ | ||
1226 | if (cmsg->Info) { | ||
1227 | printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n", | ||
1228 | card->contrnr, | ||
1229 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1230 | cmsg->Info, capi_info2str(cmsg->Info), | ||
1231 | cmsg->adr.adrPLCI); | ||
1232 | } | ||
1233 | if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) | ||
1234 | goto notfound; | ||
1235 | |||
1236 | card->bchans[plcip->chan].disconnecting = 1; | ||
1237 | break; | ||
1238 | |||
1239 | case CAPI_ALERT_CONF: /* plci */ | ||
1240 | if (cmsg->Info) { | ||
1241 | printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n", | ||
1242 | card->contrnr, | ||
1243 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1244 | cmsg->Info, capi_info2str(cmsg->Info), | ||
1245 | cmsg->adr.adrPLCI); | ||
1246 | } | ||
1247 | break; | ||
1248 | |||
1249 | case CAPI_CONNECT_IND: /* plci */ | ||
1250 | handle_incoming_call(card, cmsg); | ||
1251 | break; | ||
1252 | |||
1253 | case CAPI_CONNECT_CONF: /* plci */ | ||
1254 | if (cmsg->Info) { | ||
1255 | printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n", | ||
1256 | card->contrnr, | ||
1257 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1258 | cmsg->Info, capi_info2str(cmsg->Info), | ||
1259 | cmsg->adr.adrPLCI); | ||
1260 | } | ||
1261 | if (!(plcip = find_plci_by_msgid(card, cmsg->Messagenumber))) | ||
1262 | goto notfound; | ||
1263 | |||
1264 | plcip->plci = cmsg->adr.adrPLCI; | ||
1265 | if (cmsg->Info) { | ||
1266 | plci_change_state(card, plcip, EV_PLCI_CONNECT_CONF_ERROR); | ||
1267 | } else { | ||
1268 | plci_change_state(card, plcip, EV_PLCI_CONNECT_CONF_OK); | ||
1269 | } | ||
1270 | break; | ||
1271 | |||
1272 | case CAPI_CONNECT_ACTIVE_IND: /* plci */ | ||
1273 | |||
1274 | if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) | ||
1275 | goto notfound; | ||
1276 | |||
1277 | if (card->bchans[plcip->chan].incoming) { | ||
1278 | capi_cmsg_answer(cmsg); | ||
1279 | plci_change_state(card, plcip, EV_PLCI_CONNECT_ACTIVE_IND); | ||
1280 | send_message(card, cmsg); | ||
1281 | } else { | ||
1282 | capidrv_ncci *nccip; | ||
1283 | capi_cmsg_answer(cmsg); | ||
1284 | send_message(card, cmsg); | ||
1285 | |||
1286 | nccip = new_ncci(card, plcip, cmsg->adr.adrPLCI); | ||
1287 | |||
1288 | if (!nccip) { | ||
1289 | printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n", card->contrnr); | ||
1290 | break; /* $$$$ */ | ||
1291 | } | ||
1292 | capi_fill_CONNECT_B3_REQ(cmsg, | ||
1293 | global.ap.applid, | ||
1294 | card->msgid++, | ||
1295 | plcip->plci, /* adr */ | ||
1296 | NULL /* NCPI */ | ||
1297 | ); | ||
1298 | nccip->msgid = cmsg->Messagenumber; | ||
1299 | plci_change_state(card, plcip, | ||
1300 | EV_PLCI_CONNECT_ACTIVE_IND); | ||
1301 | ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_REQ); | ||
1302 | send_message(card, cmsg); | ||
1303 | cmd.command = ISDN_STAT_DCONN; | ||
1304 | cmd.driver = card->myid; | ||
1305 | cmd.arg = plcip->chan; | ||
1306 | card->interface.statcallb(&cmd); | ||
1307 | } | ||
1308 | break; | ||
1309 | |||
1310 | case CAPI_INFO_IND: /* Controller/plci */ | ||
1311 | |||
1312 | if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) | ||
1313 | goto notfound; | ||
1314 | |||
1315 | if (cmsg->InfoNumber == 0x4000) { | ||
1316 | if (cmsg->InfoElement[0] == 4) { | ||
1317 | cmd.command = ISDN_STAT_CINF; | ||
1318 | cmd.driver = card->myid; | ||
1319 | cmd.arg = plcip->chan; | ||
1320 | sprintf(cmd.parm.num, "%lu", | ||
1321 | (unsigned long) | ||
1322 | ((u32) cmsg->InfoElement[1] | ||
1323 | | ((u32) (cmsg->InfoElement[2]) << 8) | ||
1324 | | ((u32) (cmsg->InfoElement[3]) << 16) | ||
1325 | | ((u32) (cmsg->InfoElement[4]) << 24))); | ||
1326 | card->interface.statcallb(&cmd); | ||
1327 | break; | ||
1328 | } | ||
1329 | } | ||
1330 | cdb = capi_cmsg2str(cmsg); | ||
1331 | if (cdb) { | ||
1332 | printk(KERN_WARNING "capidrv-%d: %s\n", | ||
1333 | card->contrnr, cdb->buf); | ||
1334 | cdebbuf_free(cdb); | ||
1335 | } else | ||
1336 | printk(KERN_WARNING "capidrv-%d: CAPI_INFO_IND InfoNumber %x not handled\n", | ||
1337 | card->contrnr, cmsg->InfoNumber); | ||
1338 | |||
1339 | break; | ||
1340 | |||
1341 | case CAPI_CONNECT_ACTIVE_CONF: /* plci */ | ||
1342 | goto ignored; | ||
1343 | case CAPI_SELECT_B_PROTOCOL_CONF: /* plci */ | ||
1344 | goto ignored; | ||
1345 | case CAPI_FACILITY_IND: /* Controller/plci/ncci */ | ||
1346 | goto ignored; | ||
1347 | case CAPI_FACILITY_CONF: /* Controller/plci/ncci */ | ||
1348 | goto ignored; | ||
1349 | |||
1350 | case CAPI_INFO_CONF: /* Controller/plci */ | ||
1351 | goto ignored; | ||
1352 | |||
1353 | default: | ||
1354 | printk(KERN_ERR "capidrv-%d: got %s for plci 0x%x ???", | ||
1355 | card->contrnr, | ||
1356 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1357 | cmsg->adr.adrPLCI); | ||
1358 | } | ||
1359 | return; | ||
1360 | ignored: | ||
1361 | printk(KERN_INFO "capidrv-%d: %s for plci 0x%x ignored\n", | ||
1362 | card->contrnr, | ||
1363 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1364 | cmsg->adr.adrPLCI); | ||
1365 | return; | ||
1366 | notfound: | ||
1367 | printk(KERN_ERR "capidrv-%d: %s: plci 0x%x not found\n", | ||
1368 | card->contrnr, | ||
1369 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1370 | cmsg->adr.adrPLCI); | ||
1371 | return; | ||
1372 | } | ||
1373 | |||
1374 | static void handle_ncci(_cmsg *cmsg) | ||
1375 | { | ||
1376 | capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f); | ||
1377 | capidrv_plci *plcip; | ||
1378 | capidrv_ncci *nccip; | ||
1379 | isdn_ctrl cmd; | ||
1380 | int len; | ||
1381 | |||
1382 | if (!card) { | ||
1383 | printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n", | ||
1384 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1385 | cmsg->adr.adrController & 0x7f); | ||
1386 | return; | ||
1387 | } | ||
1388 | switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) { | ||
1389 | |||
1390 | case CAPI_CONNECT_B3_ACTIVE_IND: /* ncci */ | ||
1391 | if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) | ||
1392 | goto notfound; | ||
1393 | |||
1394 | capi_cmsg_answer(cmsg); | ||
1395 | ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_ACTIVE_IND); | ||
1396 | send_message(card, cmsg); | ||
1397 | |||
1398 | cmd.command = ISDN_STAT_BCONN; | ||
1399 | cmd.driver = card->myid; | ||
1400 | cmd.arg = nccip->chan; | ||
1401 | card->interface.statcallb(&cmd); | ||
1402 | |||
1403 | printk(KERN_INFO "capidrv-%d: chan %d up with ncci 0x%x\n", | ||
1404 | card->contrnr, nccip->chan, nccip->ncci); | ||
1405 | break; | ||
1406 | |||
1407 | case CAPI_CONNECT_B3_ACTIVE_CONF: /* ncci */ | ||
1408 | goto ignored; | ||
1409 | |||
1410 | case CAPI_CONNECT_B3_IND: /* ncci */ | ||
1411 | |||
1412 | plcip = find_plci_by_ncci(card, cmsg->adr.adrNCCI); | ||
1413 | if (plcip) { | ||
1414 | nccip = new_ncci(card, plcip, cmsg->adr.adrNCCI); | ||
1415 | if (nccip) { | ||
1416 | ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_IND); | ||
1417 | capi_fill_CONNECT_B3_RESP(cmsg, | ||
1418 | global.ap.applid, | ||
1419 | card->msgid++, | ||
1420 | nccip->ncci, /* adr */ | ||
1421 | 0, /* Reject */ | ||
1422 | NULL /* NCPI */ | ||
1423 | ); | ||
1424 | ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_RESP); | ||
1425 | send_message(card, cmsg); | ||
1426 | break; | ||
1427 | } | ||
1428 | printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n", card->contrnr); | ||
1429 | } else { | ||
1430 | printk(KERN_ERR "capidrv-%d: %s: plci for ncci 0x%x not found\n", | ||
1431 | card->contrnr, | ||
1432 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1433 | cmsg->adr.adrNCCI); | ||
1434 | } | ||
1435 | capi_fill_CONNECT_B3_RESP(cmsg, | ||
1436 | global.ap.applid, | ||
1437 | card->msgid++, | ||
1438 | cmsg->adr.adrNCCI, | ||
1439 | 2, /* Reject */ | ||
1440 | NULL /* NCPI */ | ||
1441 | ); | ||
1442 | send_message(card, cmsg); | ||
1443 | break; | ||
1444 | |||
1445 | case CAPI_CONNECT_B3_CONF: /* ncci */ | ||
1446 | |||
1447 | if (!(nccip = find_ncci_by_msgid(card, | ||
1448 | cmsg->adr.adrNCCI, | ||
1449 | cmsg->Messagenumber))) | ||
1450 | goto notfound; | ||
1451 | |||
1452 | nccip->ncci = cmsg->adr.adrNCCI; | ||
1453 | if (cmsg->Info) { | ||
1454 | printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n", | ||
1455 | card->contrnr, | ||
1456 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1457 | cmsg->Info, capi_info2str(cmsg->Info), | ||
1458 | cmsg->adr.adrNCCI); | ||
1459 | } | ||
1460 | |||
1461 | if (cmsg->Info) | ||
1462 | ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_CONF_ERROR); | ||
1463 | else | ||
1464 | ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_CONF_OK); | ||
1465 | break; | ||
1466 | |||
1467 | case CAPI_CONNECT_B3_T90_ACTIVE_IND: /* ncci */ | ||
1468 | capi_cmsg_answer(cmsg); | ||
1469 | send_message(card, cmsg); | ||
1470 | break; | ||
1471 | |||
1472 | case CAPI_DATA_B3_IND: /* ncci */ | ||
1473 | /* handled in handle_data() */ | ||
1474 | goto ignored; | ||
1475 | |||
1476 | case CAPI_DATA_B3_CONF: /* ncci */ | ||
1477 | if (cmsg->Info) { | ||
1478 | printk(KERN_WARNING "CAPI_DATA_B3_CONF: Info %x - %s\n", | ||
1479 | cmsg->Info, capi_info2str(cmsg->Info)); | ||
1480 | } | ||
1481 | if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) | ||
1482 | goto notfound; | ||
1483 | |||
1484 | len = capidrv_del_ack(nccip, cmsg->DataHandle); | ||
1485 | if (len < 0) | ||
1486 | break; | ||
1487 | cmd.command = ISDN_STAT_BSENT; | ||
1488 | cmd.driver = card->myid; | ||
1489 | cmd.arg = nccip->chan; | ||
1490 | cmd.parm.length = len; | ||
1491 | card->interface.statcallb(&cmd); | ||
1492 | break; | ||
1493 | |||
1494 | case CAPI_DISCONNECT_B3_IND: /* ncci */ | ||
1495 | if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) | ||
1496 | goto notfound; | ||
1497 | |||
1498 | card->bchans[nccip->chan].disconnecting = 1; | ||
1499 | ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_IND); | ||
1500 | capi_cmsg_answer(cmsg); | ||
1501 | ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_RESP); | ||
1502 | send_message(card, cmsg); | ||
1503 | break; | ||
1504 | |||
1505 | case CAPI_DISCONNECT_B3_CONF: /* ncci */ | ||
1506 | if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) | ||
1507 | goto notfound; | ||
1508 | if (cmsg->Info) { | ||
1509 | printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n", | ||
1510 | card->contrnr, | ||
1511 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1512 | cmsg->Info, capi_info2str(cmsg->Info), | ||
1513 | cmsg->adr.adrNCCI); | ||
1514 | ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_CONF_ERROR); | ||
1515 | } | ||
1516 | break; | ||
1517 | |||
1518 | case CAPI_RESET_B3_IND: /* ncci */ | ||
1519 | if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) | ||
1520 | goto notfound; | ||
1521 | ncci_change_state(card, nccip, EV_NCCI_RESET_B3_IND); | ||
1522 | capi_cmsg_answer(cmsg); | ||
1523 | send_message(card, cmsg); | ||
1524 | break; | ||
1525 | |||
1526 | case CAPI_RESET_B3_CONF: /* ncci */ | ||
1527 | goto ignored; /* $$$$ */ | ||
1528 | |||
1529 | case CAPI_FACILITY_IND: /* Controller/plci/ncci */ | ||
1530 | goto ignored; | ||
1531 | case CAPI_FACILITY_CONF: /* Controller/plci/ncci */ | ||
1532 | goto ignored; | ||
1533 | |||
1534 | default: | ||
1535 | printk(KERN_ERR "capidrv-%d: got %s for ncci 0x%x ???", | ||
1536 | card->contrnr, | ||
1537 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1538 | cmsg->adr.adrNCCI); | ||
1539 | } | ||
1540 | return; | ||
1541 | ignored: | ||
1542 | printk(KERN_INFO "capidrv-%d: %s for ncci 0x%x ignored\n", | ||
1543 | card->contrnr, | ||
1544 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1545 | cmsg->adr.adrNCCI); | ||
1546 | return; | ||
1547 | notfound: | ||
1548 | printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n", | ||
1549 | card->contrnr, | ||
1550 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1551 | cmsg->adr.adrNCCI); | ||
1552 | } | ||
1553 | |||
1554 | |||
1555 | static void handle_data(_cmsg *cmsg, struct sk_buff *skb) | ||
1556 | { | ||
1557 | capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f); | ||
1558 | capidrv_ncci *nccip; | ||
1559 | |||
1560 | if (!card) { | ||
1561 | printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n", | ||
1562 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1563 | cmsg->adr.adrController & 0x7f); | ||
1564 | kfree_skb(skb); | ||
1565 | return; | ||
1566 | } | ||
1567 | if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) { | ||
1568 | printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n", | ||
1569 | card->contrnr, | ||
1570 | capi_cmd2str(cmsg->Command, cmsg->Subcommand), | ||
1571 | cmsg->adr.adrNCCI); | ||
1572 | kfree_skb(skb); | ||
1573 | return; | ||
1574 | } | ||
1575 | (void) skb_pull(skb, CAPIMSG_LEN(skb->data)); | ||
1576 | card->interface.rcvcallb_skb(card->myid, nccip->chan, skb); | ||
1577 | capi_cmsg_answer(cmsg); | ||
1578 | send_message(card, cmsg); | ||
1579 | } | ||
1580 | |||
1581 | static _cmsg s_cmsg; | ||
1582 | |||
1583 | static void capidrv_recv_message(struct capi20_appl *ap, struct sk_buff *skb) | ||
1584 | { | ||
1585 | if (capi_message2cmsg(&s_cmsg, skb->data)) { | ||
1586 | printk(KERN_ERR "capidrv: applid=%d: received invalid message\n", | ||
1587 | ap->applid); | ||
1588 | kfree_skb(skb); | ||
1589 | return; | ||
1590 | } | ||
1591 | if (debugmode > 3) { | ||
1592 | _cdebbuf *cdb = capi_cmsg2str(&s_cmsg); | ||
1593 | |||
1594 | if (cdb) { | ||
1595 | printk(KERN_DEBUG "%s: applid=%d %s\n", __func__, | ||
1596 | ap->applid, cdb->buf); | ||
1597 | cdebbuf_free(cdb); | ||
1598 | } else | ||
1599 | printk(KERN_DEBUG "%s: applid=%d %s not traced\n", | ||
1600 | __func__, ap->applid, | ||
1601 | capi_cmd2str(s_cmsg.Command, s_cmsg.Subcommand)); | ||
1602 | } | ||
1603 | if (s_cmsg.Command == CAPI_DATA_B3 | ||
1604 | && s_cmsg.Subcommand == CAPI_IND) { | ||
1605 | handle_data(&s_cmsg, skb); | ||
1606 | return; | ||
1607 | } | ||
1608 | if ((s_cmsg.adr.adrController & 0xffffff00) == 0) | ||
1609 | handle_controller(&s_cmsg); | ||
1610 | else if ((s_cmsg.adr.adrPLCI & 0xffff0000) == 0) | ||
1611 | handle_plci(&s_cmsg); | ||
1612 | else | ||
1613 | handle_ncci(&s_cmsg); | ||
1614 | /* | ||
1615 | * data of skb used in s_cmsg, | ||
1616 | * free data when s_cmsg is not used again | ||
1617 | * thanks to Lars Heete <hel@admin.de> | ||
1618 | */ | ||
1619 | kfree_skb(skb); | ||
1620 | } | ||
1621 | |||
1622 | /* ------------------------------------------------------------------- */ | ||
1623 | |||
1624 | #define PUTBYTE_TO_STATUS(card, byte) \ | ||
1625 | do { \ | ||
1626 | *(card)->q931_write++ = (byte); \ | ||
1627 | if ((card)->q931_write > (card)->q931_end) \ | ||
1628 | (card)->q931_write = (card)->q931_buf; \ | ||
1629 | } while (0) | ||
1630 | |||
1631 | static void handle_dtrace_data(capidrv_contr *card, | ||
1632 | int send, int level2, u8 *data, u16 len) | ||
1633 | { | ||
1634 | u8 *p, *end; | ||
1635 | isdn_ctrl cmd; | ||
1636 | |||
1637 | if (!len) { | ||
1638 | printk(KERN_DEBUG "capidrv-%d: avmb1_q931_data: len == %d\n", | ||
1639 | card->contrnr, len); | ||
1640 | return; | ||
1641 | } | ||
1642 | |||
1643 | if (level2) { | ||
1644 | PUTBYTE_TO_STATUS(card, 'D'); | ||
1645 | PUTBYTE_TO_STATUS(card, '2'); | ||
1646 | PUTBYTE_TO_STATUS(card, send ? '>' : '<'); | ||
1647 | PUTBYTE_TO_STATUS(card, ':'); | ||
1648 | } else { | ||
1649 | PUTBYTE_TO_STATUS(card, 'D'); | ||
1650 | PUTBYTE_TO_STATUS(card, '3'); | ||
1651 | PUTBYTE_TO_STATUS(card, send ? '>' : '<'); | ||
1652 | PUTBYTE_TO_STATUS(card, ':'); | ||
1653 | } | ||
1654 | |||
1655 | for (p = data, end = data + len; p < end; p++) { | ||
1656 | PUTBYTE_TO_STATUS(card, ' '); | ||
1657 | PUTBYTE_TO_STATUS(card, hex_asc_hi(*p)); | ||
1658 | PUTBYTE_TO_STATUS(card, hex_asc_lo(*p)); | ||
1659 | } | ||
1660 | PUTBYTE_TO_STATUS(card, '\n'); | ||
1661 | |||
1662 | cmd.command = ISDN_STAT_STAVAIL; | ||
1663 | cmd.driver = card->myid; | ||
1664 | cmd.arg = len * 3 + 5; | ||
1665 | card->interface.statcallb(&cmd); | ||
1666 | } | ||
1667 | |||
1668 | /* ------------------------------------------------------------------- */ | ||
1669 | |||
1670 | static _cmsg cmdcmsg; | ||
1671 | |||
1672 | static int capidrv_ioctl(isdn_ctrl *c, capidrv_contr *card) | ||
1673 | { | ||
1674 | switch (c->arg) { | ||
1675 | case 1: | ||
1676 | debugmode = (int)(*((unsigned int *)c->parm.num)); | ||
1677 | printk(KERN_DEBUG "capidrv-%d: debugmode=%d\n", | ||
1678 | card->contrnr, debugmode); | ||
1679 | return 0; | ||
1680 | default: | ||
1681 | printk(KERN_DEBUG "capidrv-%d: capidrv_ioctl(%ld) called ??\n", | ||
1682 | card->contrnr, c->arg); | ||
1683 | return -EINVAL; | ||
1684 | } | ||
1685 | return -EINVAL; | ||
1686 | } | ||
1687 | |||
1688 | /* | ||
1689 | * Handle leased lines (CAPI-Bundling) | ||
1690 | */ | ||
1691 | |||
1692 | struct internal_bchannelinfo { | ||
1693 | unsigned short channelalloc; | ||
1694 | unsigned short operation; | ||
1695 | unsigned char cmask[31]; | ||
1696 | }; | ||
1697 | |||
1698 | static int decodeFVteln(char *teln, unsigned long *bmaskp, int *activep) | ||
1699 | { | ||
1700 | unsigned long bmask = 0; | ||
1701 | int active = !0; | ||
1702 | char *s; | ||
1703 | int i; | ||
1704 | |||
1705 | if (strncmp(teln, "FV:", 3) != 0) | ||
1706 | return 1; | ||
1707 | s = teln + 3; | ||
1708 | while (*s && *s == ' ') s++; | ||
1709 | if (!*s) return -2; | ||
1710 | if (*s == 'p' || *s == 'P') { | ||
1711 | active = 0; | ||
1712 | s++; | ||
1713 | } | ||
1714 | if (*s == 'a' || *s == 'A') { | ||
1715 | active = !0; | ||
1716 | s++; | ||
1717 | } | ||
1718 | while (*s) { | ||
1719 | int digit1 = 0; | ||
1720 | int digit2 = 0; | ||
1721 | char *endp; | ||
1722 | |||
1723 | digit1 = simple_strtoul(s, &endp, 10); | ||
1724 | if (s == endp) | ||
1725 | return -3; | ||
1726 | s = endp; | ||
1727 | |||
1728 | if (digit1 <= 0 || digit1 > 30) return -4; | ||
1729 | if (*s == 0 || *s == ',' || *s == ' ') { | ||
1730 | bmask |= (1 << digit1); | ||
1731 | digit1 = 0; | ||
1732 | if (*s) s++; | ||
1733 | continue; | ||
1734 | } | ||
1735 | if (*s != '-') return -5; | ||
1736 | s++; | ||
1737 | |||
1738 | digit2 = simple_strtoul(s, &endp, 10); | ||
1739 | if (s == endp) | ||
1740 | return -3; | ||
1741 | s = endp; | ||
1742 | |||
1743 | if (digit2 <= 0 || digit2 > 30) return -4; | ||
1744 | if (*s == 0 || *s == ',' || *s == ' ') { | ||
1745 | if (digit1 > digit2) | ||
1746 | for (i = digit2; i <= digit1; i++) | ||
1747 | bmask |= (1 << i); | ||
1748 | else | ||
1749 | for (i = digit1; i <= digit2; i++) | ||
1750 | bmask |= (1 << i); | ||
1751 | digit1 = digit2 = 0; | ||
1752 | if (*s) s++; | ||
1753 | continue; | ||
1754 | } | ||
1755 | return -6; | ||
1756 | } | ||
1757 | if (activep) *activep = active; | ||
1758 | if (bmaskp) *bmaskp = bmask; | ||
1759 | return 0; | ||
1760 | } | ||
1761 | |||
1762 | static int FVteln2capi20(char *teln, u8 AdditionalInfo[1 + 2 + 2 + 31]) | ||
1763 | { | ||
1764 | unsigned long bmask; | ||
1765 | int active; | ||
1766 | int rc, i; | ||
1767 | |||
1768 | rc = decodeFVteln(teln, &bmask, &active); | ||
1769 | if (rc) return rc; | ||
1770 | /* Length */ | ||
1771 | AdditionalInfo[0] = 2 + 2 + 31; | ||
1772 | /* Channel: 3 => use channel allocation */ | ||
1773 | AdditionalInfo[1] = 3; AdditionalInfo[2] = 0; | ||
1774 | /* Operation: 0 => DTE mode, 1 => DCE mode */ | ||
1775 | if (active) { | ||
1776 | AdditionalInfo[3] = 0; AdditionalInfo[4] = 0; | ||
1777 | } else { | ||
1778 | AdditionalInfo[3] = 1; AdditionalInfo[4] = 0; | ||
1779 | } | ||
1780 | /* Channel mask array */ | ||
1781 | AdditionalInfo[5] = 0; /* no D-Channel */ | ||
1782 | for (i = 1; i <= 30; i++) | ||
1783 | AdditionalInfo[5 + i] = (bmask & (1 << i)) ? 0xff : 0; | ||
1784 | return 0; | ||
1785 | } | ||
1786 | |||
1787 | static int capidrv_command(isdn_ctrl *c, capidrv_contr *card) | ||
1788 | { | ||
1789 | isdn_ctrl cmd; | ||
1790 | struct capidrv_bchan *bchan; | ||
1791 | struct capidrv_plci *plcip; | ||
1792 | u8 AdditionalInfo[1 + 2 + 2 + 31]; | ||
1793 | int rc, isleasedline = 0; | ||
1794 | |||
1795 | if (c->command == ISDN_CMD_IOCTL) | ||
1796 | return capidrv_ioctl(c, card); | ||
1797 | |||
1798 | switch (c->command) { | ||
1799 | case ISDN_CMD_DIAL: { | ||
1800 | u8 calling[ISDN_MSNLEN + 3]; | ||
1801 | u8 called[ISDN_MSNLEN + 2]; | ||
1802 | |||
1803 | if (debugmode) | ||
1804 | printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n", | ||
1805 | card->contrnr, | ||
1806 | c->arg, | ||
1807 | c->parm.setup.phone, | ||
1808 | c->parm.setup.si1, | ||
1809 | c->parm.setup.si2, | ||
1810 | c->parm.setup.eazmsn); | ||
1811 | |||
1812 | bchan = &card->bchans[c->arg % card->nbchan]; | ||
1813 | |||
1814 | if (bchan->plcip) { | ||
1815 | printk(KERN_ERR "capidrv-%d: dail ch=%ld,\"%s,%d,%d,%s\" in use (plci=0x%x)\n", | ||
1816 | card->contrnr, | ||
1817 | c->arg, | ||
1818 | c->parm.setup.phone, | ||
1819 | c->parm.setup.si1, | ||
1820 | c->parm.setup.si2, | ||
1821 | c->parm.setup.eazmsn, | ||
1822 | bchan->plcip->plci); | ||
1823 | return 0; | ||
1824 | } | ||
1825 | bchan->si1 = c->parm.setup.si1; | ||
1826 | bchan->si2 = c->parm.setup.si2; | ||
1827 | |||
1828 | strncpy(bchan->num, c->parm.setup.phone, sizeof(bchan->num)); | ||
1829 | strncpy(bchan->mynum, c->parm.setup.eazmsn, sizeof(bchan->mynum)); | ||
1830 | rc = FVteln2capi20(bchan->num, AdditionalInfo); | ||
1831 | isleasedline = (rc == 0); | ||
1832 | if (rc < 0) | ||
1833 | printk(KERN_ERR "capidrv-%d: WARNING: invalid leased linedefinition \"%s\"\n", card->contrnr, bchan->num); | ||
1834 | |||
1835 | if (isleasedline) { | ||
1836 | calling[0] = 0; | ||
1837 | called[0] = 0; | ||
1838 | if (debugmode) | ||
1839 | printk(KERN_DEBUG "capidrv-%d: connecting leased line\n", card->contrnr); | ||
1840 | } else { | ||
1841 | calling[0] = strlen(bchan->mynum) + 2; | ||
1842 | calling[1] = 0; | ||
1843 | calling[2] = 0x80; | ||
1844 | strncpy(calling + 3, bchan->mynum, ISDN_MSNLEN); | ||
1845 | called[0] = strlen(bchan->num) + 1; | ||
1846 | called[1] = 0x80; | ||
1847 | strncpy(called + 2, bchan->num, ISDN_MSNLEN); | ||
1848 | } | ||
1849 | |||
1850 | capi_fill_CONNECT_REQ(&cmdcmsg, | ||
1851 | global.ap.applid, | ||
1852 | card->msgid++, | ||
1853 | card->contrnr, /* adr */ | ||
1854 | si2cip(bchan->si1, bchan->si2), /* cipvalue */ | ||
1855 | called, /* CalledPartyNumber */ | ||
1856 | calling, /* CallingPartyNumber */ | ||
1857 | NULL, /* CalledPartySubaddress */ | ||
1858 | NULL, /* CallingPartySubaddress */ | ||
1859 | b1prot(bchan->l2, bchan->l3), /* B1protocol */ | ||
1860 | b2prot(bchan->l2, bchan->l3), /* B2protocol */ | ||
1861 | b3prot(bchan->l2, bchan->l3), /* B3protocol */ | ||
1862 | b1config(bchan->l2, bchan->l3), /* B1configuration */ | ||
1863 | NULL, /* B2configuration */ | ||
1864 | NULL, /* B3configuration */ | ||
1865 | NULL, /* BC */ | ||
1866 | NULL, /* LLC */ | ||
1867 | NULL, /* HLC */ | ||
1868 | /* BChannelinformation */ | ||
1869 | isleasedline ? AdditionalInfo : NULL, | ||
1870 | NULL, /* Keypadfacility */ | ||
1871 | NULL, /* Useruserdata */ | ||
1872 | NULL /* Facilitydataarray */ | ||
1873 | ); | ||
1874 | if ((plcip = new_plci(card, (c->arg % card->nbchan))) == NULL) { | ||
1875 | cmd.command = ISDN_STAT_DHUP; | ||
1876 | cmd.driver = card->myid; | ||
1877 | cmd.arg = (c->arg % card->nbchan); | ||
1878 | card->interface.statcallb(&cmd); | ||
1879 | return -1; | ||
1880 | } | ||
1881 | plcip->msgid = cmdcmsg.Messagenumber; | ||
1882 | plcip->leasedline = isleasedline; | ||
1883 | plci_change_state(card, plcip, EV_PLCI_CONNECT_REQ); | ||
1884 | send_message(card, &cmdcmsg); | ||
1885 | return 0; | ||
1886 | } | ||
1887 | |||
1888 | case ISDN_CMD_ACCEPTD: | ||
1889 | |||
1890 | bchan = &card->bchans[c->arg % card->nbchan]; | ||
1891 | if (debugmode) | ||
1892 | printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTD(ch=%ld) l2=%d l3=%d\n", | ||
1893 | card->contrnr, | ||
1894 | c->arg, bchan->l2, bchan->l3); | ||
1895 | |||
1896 | capi_fill_CONNECT_RESP(&cmdcmsg, | ||
1897 | global.ap.applid, | ||
1898 | card->msgid++, | ||
1899 | bchan->plcip->plci, /* adr */ | ||
1900 | 0, /* Reject */ | ||
1901 | b1prot(bchan->l2, bchan->l3), /* B1protocol */ | ||
1902 | b2prot(bchan->l2, bchan->l3), /* B2protocol */ | ||
1903 | b3prot(bchan->l2, bchan->l3), /* B3protocol */ | ||
1904 | b1config(bchan->l2, bchan->l3), /* B1configuration */ | ||
1905 | NULL, /* B2configuration */ | ||
1906 | NULL, /* B3configuration */ | ||
1907 | NULL, /* ConnectedNumber */ | ||
1908 | NULL, /* ConnectedSubaddress */ | ||
1909 | NULL, /* LLC */ | ||
1910 | NULL, /* BChannelinformation */ | ||
1911 | NULL, /* Keypadfacility */ | ||
1912 | NULL, /* Useruserdata */ | ||
1913 | NULL /* Facilitydataarray */ | ||
1914 | ); | ||
1915 | if (capi_cmsg2message(&cmdcmsg, cmdcmsg.buf)) { | ||
1916 | printk(KERN_ERR "capidrv-%d: capidrv_command: parser failure\n", | ||
1917 | card->contrnr); | ||
1918 | return -EINVAL; | ||
1919 | } | ||
1920 | plci_change_state(card, bchan->plcip, EV_PLCI_CONNECT_RESP); | ||
1921 | send_message(card, &cmdcmsg); | ||
1922 | return 0; | ||
1923 | |||
1924 | case ISDN_CMD_ACCEPTB: | ||
1925 | if (debugmode) | ||
1926 | printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTB(ch=%ld)\n", | ||
1927 | card->contrnr, | ||
1928 | c->arg); | ||
1929 | return -ENOSYS; | ||
1930 | |||
1931 | case ISDN_CMD_HANGUP: | ||
1932 | if (debugmode) | ||
1933 | printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_HANGUP(ch=%ld)\n", | ||
1934 | card->contrnr, | ||
1935 | c->arg); | ||
1936 | bchan = &card->bchans[c->arg % card->nbchan]; | ||
1937 | |||
1938 | if (bchan->disconnecting) { | ||
1939 | if (debugmode) | ||
1940 | printk(KERN_DEBUG "capidrv-%d: chan %ld already disconnecting ...\n", | ||
1941 | card->contrnr, | ||
1942 | c->arg); | ||
1943 | return 0; | ||
1944 | } | ||
1945 | if (bchan->nccip) { | ||
1946 | bchan->disconnecting = 1; | ||
1947 | capi_fill_DISCONNECT_B3_REQ(&cmdcmsg, | ||
1948 | global.ap.applid, | ||
1949 | card->msgid++, | ||
1950 | bchan->nccip->ncci, | ||
1951 | NULL /* NCPI */ | ||
1952 | ); | ||
1953 | ncci_change_state(card, bchan->nccip, EV_NCCI_DISCONNECT_B3_REQ); | ||
1954 | send_message(card, &cmdcmsg); | ||
1955 | return 0; | ||
1956 | } else if (bchan->plcip) { | ||
1957 | if (bchan->plcip->state == ST_PLCI_INCOMING) { | ||
1958 | /* | ||
1959 | * just ignore, we a called from | ||
1960 | * isdn_status_callback(), | ||
1961 | * which will return 0 or 2, this is handled | ||
1962 | * by the CONNECT_IND handler | ||
1963 | */ | ||
1964 | bchan->disconnecting = 1; | ||
1965 | return 0; | ||
1966 | } else if (bchan->plcip->plci) { | ||
1967 | bchan->disconnecting = 1; | ||
1968 | capi_fill_DISCONNECT_REQ(&cmdcmsg, | ||
1969 | global.ap.applid, | ||
1970 | card->msgid++, | ||
1971 | bchan->plcip->plci, | ||
1972 | NULL, /* BChannelinformation */ | ||
1973 | NULL, /* Keypadfacility */ | ||
1974 | NULL, /* Useruserdata */ | ||
1975 | NULL /* Facilitydataarray */ | ||
1976 | ); | ||
1977 | plci_change_state(card, bchan->plcip, EV_PLCI_DISCONNECT_REQ); | ||
1978 | send_message(card, &cmdcmsg); | ||
1979 | return 0; | ||
1980 | } else { | ||
1981 | printk(KERN_ERR "capidrv-%d: chan %ld disconnect request while waiting for CONNECT_CONF\n", | ||
1982 | card->contrnr, | ||
1983 | c->arg); | ||
1984 | return -EINVAL; | ||
1985 | } | ||
1986 | } | ||
1987 | printk(KERN_ERR "capidrv-%d: chan %ld disconnect request on free channel\n", | ||
1988 | card->contrnr, | ||
1989 | c->arg); | ||
1990 | return -EINVAL; | ||
1991 | /* ready */ | ||
1992 | |||
1993 | case ISDN_CMD_SETL2: | ||
1994 | if (debugmode) | ||
1995 | printk(KERN_DEBUG "capidrv-%d: set L2 on chan %ld to %ld\n", | ||
1996 | card->contrnr, | ||
1997 | (c->arg & 0xff), (c->arg >> 8)); | ||
1998 | bchan = &card->bchans[(c->arg & 0xff) % card->nbchan]; | ||
1999 | bchan->l2 = (c->arg >> 8); | ||
2000 | return 0; | ||
2001 | |||
2002 | case ISDN_CMD_SETL3: | ||
2003 | if (debugmode) | ||
2004 | printk(KERN_DEBUG "capidrv-%d: set L3 on chan %ld to %ld\n", | ||
2005 | card->contrnr, | ||
2006 | (c->arg & 0xff), (c->arg >> 8)); | ||
2007 | bchan = &card->bchans[(c->arg & 0xff) % card->nbchan]; | ||
2008 | bchan->l3 = (c->arg >> 8); | ||
2009 | return 0; | ||
2010 | |||
2011 | case ISDN_CMD_SETEAZ: | ||
2012 | if (debugmode) | ||
2013 | printk(KERN_DEBUG "capidrv-%d: set EAZ \"%s\" on chan %ld\n", | ||
2014 | card->contrnr, | ||
2015 | c->parm.num, c->arg); | ||
2016 | bchan = &card->bchans[c->arg % card->nbchan]; | ||
2017 | strncpy(bchan->msn, c->parm.num, ISDN_MSNLEN); | ||
2018 | return 0; | ||
2019 | |||
2020 | case ISDN_CMD_CLREAZ: | ||
2021 | if (debugmode) | ||
2022 | printk(KERN_DEBUG "capidrv-%d: clearing EAZ on chan %ld\n", | ||
2023 | card->contrnr, c->arg); | ||
2024 | bchan = &card->bchans[c->arg % card->nbchan]; | ||
2025 | bchan->msn[0] = 0; | ||
2026 | return 0; | ||
2027 | |||
2028 | default: | ||
2029 | printk(KERN_ERR "capidrv-%d: ISDN_CMD_%d, Huh?\n", | ||
2030 | card->contrnr, c->command); | ||
2031 | return -EINVAL; | ||
2032 | } | ||
2033 | return 0; | ||
2034 | } | ||
2035 | |||
2036 | static int if_command(isdn_ctrl *c) | ||
2037 | { | ||
2038 | capidrv_contr *card = findcontrbydriverid(c->driver); | ||
2039 | |||
2040 | if (card) | ||
2041 | return capidrv_command(c, card); | ||
2042 | |||
2043 | printk(KERN_ERR | ||
2044 | "capidrv: if_command %d called with invalid driverId %d!\n", | ||
2045 | c->command, c->driver); | ||
2046 | return -ENODEV; | ||
2047 | } | ||
2048 | |||
2049 | static _cmsg sendcmsg; | ||
2050 | |||
2051 | static int if_sendbuf(int id, int channel, int doack, struct sk_buff *skb) | ||
2052 | { | ||
2053 | capidrv_contr *card = findcontrbydriverid(id); | ||
2054 | capidrv_bchan *bchan; | ||
2055 | capidrv_ncci *nccip; | ||
2056 | int len = skb->len; | ||
2057 | int msglen; | ||
2058 | u16 errcode; | ||
2059 | u16 datahandle; | ||
2060 | u32 data; | ||
2061 | |||
2062 | if (!card) { | ||
2063 | printk(KERN_ERR "capidrv: if_sendbuf called with invalid driverId %d!\n", | ||
2064 | id); | ||
2065 | return 0; | ||
2066 | } | ||
2067 | if (debugmode > 4) | ||
2068 | printk(KERN_DEBUG "capidrv-%d: sendbuf len=%d skb=%p doack=%d\n", | ||
2069 | card->contrnr, len, skb, doack); | ||
2070 | bchan = &card->bchans[channel % card->nbchan]; | ||
2071 | nccip = bchan->nccip; | ||
2072 | if (!nccip || nccip->state != ST_NCCI_ACTIVE) { | ||
2073 | printk(KERN_ERR "capidrv-%d: if_sendbuf: %s:%d: chan not up!\n", | ||
2074 | card->contrnr, card->name, channel); | ||
2075 | return 0; | ||
2076 | } | ||
2077 | datahandle = nccip->datahandle; | ||
2078 | |||
2079 | /* | ||
2080 | * Here we copy pointer skb->data into the 32-bit 'Data' field. | ||
2081 | * The 'Data' field is not used in practice in linux kernel | ||
2082 | * (neither in 32 or 64 bit), but should have some value, | ||
2083 | * since a CAPI message trace will display it. | ||
2084 | * | ||
2085 | * The correct value in the 32 bit case is the address of the | ||
2086 | * data, in 64 bit it makes no sense, we use 0 there. | ||
2087 | */ | ||
2088 | |||
2089 | #ifdef CONFIG_64BIT | ||
2090 | data = 0; | ||
2091 | #else | ||
2092 | data = (unsigned long) skb->data; | ||
2093 | #endif | ||
2094 | |||
2095 | capi_fill_DATA_B3_REQ(&sendcmsg, global.ap.applid, card->msgid++, | ||
2096 | nccip->ncci, /* adr */ | ||
2097 | data, /* Data */ | ||
2098 | skb->len, /* DataLength */ | ||
2099 | datahandle, /* DataHandle */ | ||
2100 | 0 /* Flags */ | ||
2101 | ); | ||
2102 | |||
2103 | if (capidrv_add_ack(nccip, datahandle, doack ? (int)skb->len : -1) < 0) | ||
2104 | return 0; | ||
2105 | |||
2106 | if (capi_cmsg2message(&sendcmsg, sendcmsg.buf)) { | ||
2107 | printk(KERN_ERR "capidrv-%d: if_sendbuf: parser failure\n", | ||
2108 | card->contrnr); | ||
2109 | return -EINVAL; | ||
2110 | } | ||
2111 | msglen = CAPIMSG_LEN(sendcmsg.buf); | ||
2112 | if (skb_headroom(skb) < msglen) { | ||
2113 | struct sk_buff *nskb = skb_realloc_headroom(skb, msglen); | ||
2114 | if (!nskb) { | ||
2115 | printk(KERN_ERR "capidrv-%d: if_sendbuf: no memory\n", | ||
2116 | card->contrnr); | ||
2117 | (void)capidrv_del_ack(nccip, datahandle); | ||
2118 | return 0; | ||
2119 | } | ||
2120 | printk(KERN_DEBUG "capidrv-%d: only %d bytes headroom, need %d\n", | ||
2121 | card->contrnr, skb_headroom(skb), msglen); | ||
2122 | memcpy(skb_push(nskb, msglen), sendcmsg.buf, msglen); | ||
2123 | errcode = capi20_put_message(&global.ap, nskb); | ||
2124 | if (errcode == CAPI_NOERROR) { | ||
2125 | dev_kfree_skb(skb); | ||
2126 | nccip->datahandle++; | ||
2127 | return len; | ||
2128 | } | ||
2129 | if (debugmode > 3) | ||
2130 | printk(KERN_DEBUG "capidrv-%d: sendbuf putmsg ret(%x) - %s\n", | ||
2131 | card->contrnr, errcode, capi_info2str(errcode)); | ||
2132 | (void)capidrv_del_ack(nccip, datahandle); | ||
2133 | dev_kfree_skb(nskb); | ||
2134 | return errcode == CAPI_SENDQUEUEFULL ? 0 : -1; | ||
2135 | } else { | ||
2136 | memcpy(skb_push(skb, msglen), sendcmsg.buf, msglen); | ||
2137 | errcode = capi20_put_message(&global.ap, skb); | ||
2138 | if (errcode == CAPI_NOERROR) { | ||
2139 | nccip->datahandle++; | ||
2140 | return len; | ||
2141 | } | ||
2142 | if (debugmode > 3) | ||
2143 | printk(KERN_DEBUG "capidrv-%d: sendbuf putmsg ret(%x) - %s\n", | ||
2144 | card->contrnr, errcode, capi_info2str(errcode)); | ||
2145 | skb_pull(skb, msglen); | ||
2146 | (void)capidrv_del_ack(nccip, datahandle); | ||
2147 | return errcode == CAPI_SENDQUEUEFULL ? 0 : -1; | ||
2148 | } | ||
2149 | } | ||
2150 | |||
2151 | static int if_readstat(u8 __user *buf, int len, int id, int channel) | ||
2152 | { | ||
2153 | capidrv_contr *card = findcontrbydriverid(id); | ||
2154 | int count; | ||
2155 | u8 __user *p; | ||
2156 | |||
2157 | if (!card) { | ||
2158 | printk(KERN_ERR "capidrv: if_readstat called with invalid driverId %d!\n", | ||
2159 | id); | ||
2160 | return -ENODEV; | ||
2161 | } | ||
2162 | |||
2163 | for (p = buf, count = 0; count < len; p++, count++) { | ||
2164 | if (put_user(*card->q931_read++, p)) | ||
2165 | return -EFAULT; | ||
2166 | if (card->q931_read > card->q931_end) | ||
2167 | card->q931_read = card->q931_buf; | ||
2168 | } | ||
2169 | return count; | ||
2170 | |||
2171 | } | ||
2172 | |||
2173 | static void enable_dchannel_trace(capidrv_contr *card) | ||
2174 | { | ||
2175 | u8 manufacturer[CAPI_MANUFACTURER_LEN]; | ||
2176 | capi_version version; | ||
2177 | u16 contr = card->contrnr; | ||
2178 | u16 errcode; | ||
2179 | u16 avmversion[3]; | ||
2180 | |||
2181 | errcode = capi20_get_manufacturer(contr, manufacturer); | ||
2182 | if (errcode != CAPI_NOERROR) { | ||
2183 | printk(KERN_ERR "%s: can't get manufacturer (0x%x)\n", | ||
2184 | card->name, errcode); | ||
2185 | return; | ||
2186 | } | ||
2187 | if (strstr(manufacturer, "AVM") == NULL) { | ||
2188 | printk(KERN_ERR "%s: not from AVM, no d-channel trace possible (%s)\n", | ||
2189 | card->name, manufacturer); | ||
2190 | return; | ||
2191 | } | ||
2192 | errcode = capi20_get_version(contr, &version); | ||
2193 | if (errcode != CAPI_NOERROR) { | ||
2194 | printk(KERN_ERR "%s: can't get version (0x%x)\n", | ||
2195 | card->name, errcode); | ||
2196 | return; | ||
2197 | } | ||
2198 | avmversion[0] = (version.majormanuversion >> 4) & 0x0f; | ||
2199 | avmversion[1] = (version.majormanuversion << 4) & 0xf0; | ||
2200 | avmversion[1] |= (version.minormanuversion >> 4) & 0x0f; | ||
2201 | avmversion[2] |= version.minormanuversion & 0x0f; | ||
2202 | |||
2203 | if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) { | ||
2204 | printk(KERN_INFO "%s: D2 trace enabled\n", card->name); | ||
2205 | capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.ap.applid, | ||
2206 | card->msgid++, | ||
2207 | contr, | ||
2208 | 0x214D5641, /* ManuID */ | ||
2209 | 0, /* Class */ | ||
2210 | 1, /* Function */ | ||
2211 | (_cstruct)"\004\200\014\000\000"); | ||
2212 | } else { | ||
2213 | printk(KERN_INFO "%s: D3 trace enabled\n", card->name); | ||
2214 | capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.ap.applid, | ||
2215 | card->msgid++, | ||
2216 | contr, | ||
2217 | 0x214D5641, /* ManuID */ | ||
2218 | 0, /* Class */ | ||
2219 | 1, /* Function */ | ||
2220 | (_cstruct)"\004\002\003\000\000"); | ||
2221 | } | ||
2222 | send_message(card, &cmdcmsg); | ||
2223 | } | ||
2224 | |||
2225 | |||
2226 | static void send_listen(capidrv_contr *card) | ||
2227 | { | ||
2228 | capi_fill_LISTEN_REQ(&cmdcmsg, global.ap.applid, | ||
2229 | card->msgid++, | ||
2230 | card->contrnr, /* controller */ | ||
2231 | 1 << 6, /* Infomask */ | ||
2232 | card->cipmask, | ||
2233 | card->cipmask2, | ||
2234 | NULL, NULL); | ||
2235 | listen_change_state(card, EV_LISTEN_REQ); | ||
2236 | send_message(card, &cmdcmsg); | ||
2237 | } | ||
2238 | |||
2239 | static void listentimerfunc(struct timer_list *t) | ||
2240 | { | ||
2241 | capidrv_contr *card = from_timer(card, t, listentimer); | ||
2242 | if (card->state != ST_LISTEN_NONE && card->state != ST_LISTEN_ACTIVE) | ||
2243 | printk(KERN_ERR "%s: controller dead ??\n", card->name); | ||
2244 | send_listen(card); | ||
2245 | mod_timer(&card->listentimer, jiffies + 60 * HZ); | ||
2246 | } | ||
2247 | |||
2248 | |||
2249 | static int capidrv_addcontr(u16 contr, struct capi_profile *profp) | ||
2250 | { | ||
2251 | capidrv_contr *card; | ||
2252 | unsigned long flags; | ||
2253 | isdn_ctrl cmd; | ||
2254 | char id[20]; | ||
2255 | int i; | ||
2256 | |||
2257 | sprintf(id, "capidrv-%d", contr); | ||
2258 | if (!try_module_get(THIS_MODULE)) { | ||
2259 | printk(KERN_WARNING "capidrv: (%s) Could not reserve module\n", id); | ||
2260 | return -1; | ||
2261 | } | ||
2262 | if (!(card = kzalloc(sizeof(capidrv_contr), GFP_ATOMIC))) { | ||
2263 | printk(KERN_WARNING | ||
2264 | "capidrv: (%s) Could not allocate contr-struct.\n", id); | ||
2265 | return -1; | ||
2266 | } | ||
2267 | card->owner = THIS_MODULE; | ||
2268 | timer_setup(&card->listentimer, listentimerfunc, 0); | ||
2269 | strcpy(card->name, id); | ||
2270 | card->contrnr = contr; | ||
2271 | card->nbchan = profp->nbchannel; | ||
2272 | card->bchans = kmalloc_array(card->nbchan, sizeof(capidrv_bchan), | ||
2273 | GFP_ATOMIC); | ||
2274 | if (!card->bchans) { | ||
2275 | printk(KERN_WARNING | ||
2276 | "capidrv: (%s) Could not allocate bchan-structs.\n", id); | ||
2277 | module_put(card->owner); | ||
2278 | kfree(card); | ||
2279 | return -1; | ||
2280 | } | ||
2281 | card->interface.channels = profp->nbchannel; | ||
2282 | card->interface.maxbufsize = 2048; | ||
2283 | card->interface.command = if_command; | ||
2284 | card->interface.writebuf_skb = if_sendbuf; | ||
2285 | card->interface.writecmd = NULL; | ||
2286 | card->interface.readstat = if_readstat; | ||
2287 | card->interface.features = | ||
2288 | ISDN_FEATURE_L2_HDLC | | ||
2289 | ISDN_FEATURE_L2_TRANS | | ||
2290 | ISDN_FEATURE_L3_TRANS | | ||
2291 | ISDN_FEATURE_P_UNKNOWN | | ||
2292 | ISDN_FEATURE_L2_X75I | | ||
2293 | ISDN_FEATURE_L2_X75UI | | ||
2294 | ISDN_FEATURE_L2_X75BUI; | ||
2295 | if (profp->support1 & (1 << 2)) | ||
2296 | card->interface.features |= | ||
2297 | ISDN_FEATURE_L2_V11096 | | ||
2298 | ISDN_FEATURE_L2_V11019 | | ||
2299 | ISDN_FEATURE_L2_V11038; | ||
2300 | if (profp->support1 & (1 << 8)) | ||
2301 | card->interface.features |= ISDN_FEATURE_L2_MODEM; | ||
2302 | card->interface.hl_hdrlen = 22; /* len of DATA_B3_REQ */ | ||
2303 | strncpy(card->interface.id, id, sizeof(card->interface.id) - 1); | ||
2304 | |||
2305 | |||
2306 | card->q931_read = card->q931_buf; | ||
2307 | card->q931_write = card->q931_buf; | ||
2308 | card->q931_end = card->q931_buf + sizeof(card->q931_buf) - 1; | ||
2309 | |||
2310 | if (!register_isdn(&card->interface)) { | ||
2311 | printk(KERN_ERR "capidrv: Unable to register contr %s\n", id); | ||
2312 | kfree(card->bchans); | ||
2313 | module_put(card->owner); | ||
2314 | kfree(card); | ||
2315 | return -1; | ||
2316 | } | ||
2317 | card->myid = card->interface.channels; | ||
2318 | memset(card->bchans, 0, sizeof(capidrv_bchan) * card->nbchan); | ||
2319 | for (i = 0; i < card->nbchan; i++) { | ||
2320 | card->bchans[i].contr = card; | ||
2321 | } | ||
2322 | |||
2323 | spin_lock_irqsave(&global_lock, flags); | ||
2324 | card->next = global.contr_list; | ||
2325 | global.contr_list = card; | ||
2326 | global.ncontr++; | ||
2327 | spin_unlock_irqrestore(&global_lock, flags); | ||
2328 | |||
2329 | cmd.command = ISDN_STAT_RUN; | ||
2330 | cmd.driver = card->myid; | ||
2331 | card->interface.statcallb(&cmd); | ||
2332 | |||
2333 | card->cipmask = 0x1FFF03FF; /* any */ | ||
2334 | card->cipmask2 = 0; | ||
2335 | |||
2336 | send_listen(card); | ||
2337 | mod_timer(&card->listentimer, jiffies + 60 * HZ); | ||
2338 | |||
2339 | printk(KERN_INFO "%s: now up (%d B channels)\n", | ||
2340 | card->name, card->nbchan); | ||
2341 | |||
2342 | enable_dchannel_trace(card); | ||
2343 | |||
2344 | return 0; | ||
2345 | } | ||
2346 | |||
2347 | static int capidrv_delcontr(u16 contr) | ||
2348 | { | ||
2349 | capidrv_contr **pp, *card; | ||
2350 | unsigned long flags; | ||
2351 | isdn_ctrl cmd; | ||
2352 | |||
2353 | spin_lock_irqsave(&global_lock, flags); | ||
2354 | for (card = global.contr_list; card; card = card->next) { | ||
2355 | if (card->contrnr == contr) | ||
2356 | break; | ||
2357 | } | ||
2358 | if (!card) { | ||
2359 | spin_unlock_irqrestore(&global_lock, flags); | ||
2360 | printk(KERN_ERR "capidrv: delcontr: no contr %u\n", contr); | ||
2361 | return -1; | ||
2362 | } | ||
2363 | |||
2364 | /* FIXME: maybe a race condition the card should be removed | ||
2365 | * here from global list /kkeil | ||
2366 | */ | ||
2367 | spin_unlock_irqrestore(&global_lock, flags); | ||
2368 | |||
2369 | del_timer(&card->listentimer); | ||
2370 | |||
2371 | if (debugmode) | ||
2372 | printk(KERN_DEBUG "capidrv-%d: id=%d unloading\n", | ||
2373 | card->contrnr, card->myid); | ||
2374 | |||
2375 | cmd.command = ISDN_STAT_STOP; | ||
2376 | cmd.driver = card->myid; | ||
2377 | card->interface.statcallb(&cmd); | ||
2378 | |||
2379 | while (card->nbchan) { | ||
2380 | |||
2381 | cmd.command = ISDN_STAT_DISCH; | ||
2382 | cmd.driver = card->myid; | ||
2383 | cmd.arg = card->nbchan - 1; | ||
2384 | cmd.parm.num[0] = 0; | ||
2385 | if (debugmode) | ||
2386 | printk(KERN_DEBUG "capidrv-%d: id=%d disable chan=%ld\n", | ||
2387 | card->contrnr, card->myid, cmd.arg); | ||
2388 | card->interface.statcallb(&cmd); | ||
2389 | |||
2390 | if (card->bchans[card->nbchan - 1].nccip) | ||
2391 | free_ncci(card, card->bchans[card->nbchan - 1].nccip); | ||
2392 | if (card->bchans[card->nbchan - 1].plcip) | ||
2393 | free_plci(card, card->bchans[card->nbchan - 1].plcip); | ||
2394 | if (card->plci_list) | ||
2395 | printk(KERN_ERR "capidrv: bug in free_plci()\n"); | ||
2396 | card->nbchan--; | ||
2397 | } | ||
2398 | kfree(card->bchans); | ||
2399 | card->bchans = NULL; | ||
2400 | |||
2401 | if (debugmode) | ||
2402 | printk(KERN_DEBUG "capidrv-%d: id=%d isdn unload\n", | ||
2403 | card->contrnr, card->myid); | ||
2404 | |||
2405 | cmd.command = ISDN_STAT_UNLOAD; | ||
2406 | cmd.driver = card->myid; | ||
2407 | card->interface.statcallb(&cmd); | ||
2408 | |||
2409 | if (debugmode) | ||
2410 | printk(KERN_DEBUG "capidrv-%d: id=%d remove contr from list\n", | ||
2411 | card->contrnr, card->myid); | ||
2412 | |||
2413 | spin_lock_irqsave(&global_lock, flags); | ||
2414 | for (pp = &global.contr_list; *pp; pp = &(*pp)->next) { | ||
2415 | if (*pp == card) { | ||
2416 | *pp = (*pp)->next; | ||
2417 | card->next = NULL; | ||
2418 | global.ncontr--; | ||
2419 | break; | ||
2420 | } | ||
2421 | } | ||
2422 | spin_unlock_irqrestore(&global_lock, flags); | ||
2423 | |||
2424 | module_put(card->owner); | ||
2425 | printk(KERN_INFO "%s: now down.\n", card->name); | ||
2426 | kfree(card); | ||
2427 | return 0; | ||
2428 | } | ||
2429 | |||
2430 | |||
2431 | static int | ||
2432 | lower_callback(struct notifier_block *nb, unsigned long val, void *v) | ||
2433 | { | ||
2434 | capi_profile profile; | ||
2435 | u32 contr = (long)v; | ||
2436 | |||
2437 | switch (val) { | ||
2438 | case CAPICTR_UP: | ||
2439 | printk(KERN_INFO "capidrv: controller %hu up\n", contr); | ||
2440 | if (capi20_get_profile(contr, &profile) == CAPI_NOERROR) | ||
2441 | (void) capidrv_addcontr(contr, &profile); | ||
2442 | break; | ||
2443 | case CAPICTR_DOWN: | ||
2444 | printk(KERN_INFO "capidrv: controller %hu down\n", contr); | ||
2445 | (void) capidrv_delcontr(contr); | ||
2446 | break; | ||
2447 | } | ||
2448 | return NOTIFY_OK; | ||
2449 | } | ||
2450 | |||
2451 | /* | ||
2452 | * /proc/capi/capidrv: | ||
2453 | * nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt | ||
2454 | */ | ||
2455 | static int __maybe_unused capidrv_proc_show(struct seq_file *m, void *v) | ||
2456 | { | ||
2457 | seq_printf(m, "%lu %lu %lu %lu\n", | ||
2458 | global.ap.nrecvctlpkt, | ||
2459 | global.ap.nrecvdatapkt, | ||
2460 | global.ap.nsentctlpkt, | ||
2461 | global.ap.nsentdatapkt); | ||
2462 | return 0; | ||
2463 | } | ||
2464 | |||
2465 | static void __init proc_init(void) | ||
2466 | { | ||
2467 | proc_create_single("capi/capidrv", 0, NULL, capidrv_proc_show); | ||
2468 | } | ||
2469 | |||
2470 | static void __exit proc_exit(void) | ||
2471 | { | ||
2472 | remove_proc_entry("capi/capidrv", NULL); | ||
2473 | } | ||
2474 | |||
2475 | static struct notifier_block capictr_nb = { | ||
2476 | .notifier_call = lower_callback, | ||
2477 | }; | ||
2478 | |||
2479 | static int __init capidrv_init(void) | ||
2480 | { | ||
2481 | capi_profile profile; | ||
2482 | u32 ncontr, contr; | ||
2483 | u16 errcode; | ||
2484 | |||
2485 | global.ap.rparam.level3cnt = -2; /* number of bchannels twice */ | ||
2486 | global.ap.rparam.datablkcnt = 16; | ||
2487 | global.ap.rparam.datablklen = 2048; | ||
2488 | |||
2489 | global.ap.recv_message = capidrv_recv_message; | ||
2490 | errcode = capi20_register(&global.ap); | ||
2491 | if (errcode) { | ||
2492 | return -EIO; | ||
2493 | } | ||
2494 | |||
2495 | register_capictr_notifier(&capictr_nb); | ||
2496 | |||
2497 | errcode = capi20_get_profile(0, &profile); | ||
2498 | if (errcode != CAPI_NOERROR) { | ||
2499 | unregister_capictr_notifier(&capictr_nb); | ||
2500 | capi20_release(&global.ap); | ||
2501 | return -EIO; | ||
2502 | } | ||
2503 | |||
2504 | ncontr = profile.ncontroller; | ||
2505 | for (contr = 1; contr <= ncontr; contr++) { | ||
2506 | errcode = capi20_get_profile(contr, &profile); | ||
2507 | if (errcode != CAPI_NOERROR) | ||
2508 | continue; | ||
2509 | (void) capidrv_addcontr(contr, &profile); | ||
2510 | } | ||
2511 | proc_init(); | ||
2512 | |||
2513 | return 0; | ||
2514 | } | ||
2515 | |||
2516 | static void __exit capidrv_exit(void) | ||
2517 | { | ||
2518 | unregister_capictr_notifier(&capictr_nb); | ||
2519 | capi20_release(&global.ap); | ||
2520 | |||
2521 | proc_exit(); | ||
2522 | } | ||
2523 | |||
2524 | module_init(capidrv_init); | ||
2525 | module_exit(capidrv_exit); | ||
diff --git a/drivers/isdn/capi/capidrv.h b/drivers/isdn/capi/capidrv.h deleted file mode 100644 index 4466b2e0176d..000000000000 --- a/drivers/isdn/capi/capidrv.h +++ /dev/null | |||
@@ -1,140 +0,0 @@ | |||
1 | /* $Id: capidrv.h,v 1.2.8.2 2001/09/23 22:24:33 kai Exp $ | ||
2 | * | ||
3 | * ISDN4Linux Driver, using capi20 interface (kernelcapi) | ||
4 | * | ||
5 | * Copyright 1997 by Carsten Paeth <calle@calle.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 __CAPIDRV_H__ | ||
13 | #define __CAPIDRV_H__ | ||
14 | |||
15 | /* | ||
16 | * LISTEN state machine | ||
17 | */ | ||
18 | #define ST_LISTEN_NONE 0 /* L-0 */ | ||
19 | #define ST_LISTEN_WAIT_CONF 1 /* L-0.1 */ | ||
20 | #define ST_LISTEN_ACTIVE 2 /* L-1 */ | ||
21 | #define ST_LISTEN_ACTIVE_WAIT_CONF 3 /* L-1.1 */ | ||
22 | |||
23 | |||
24 | #define EV_LISTEN_REQ 1 /* L-0 -> L-0.1 | ||
25 | L-1 -> L-1.1 */ | ||
26 | #define EV_LISTEN_CONF_ERROR 2 /* L-0.1 -> L-0 | ||
27 | L-1.1 -> L-1 */ | ||
28 | #define EV_LISTEN_CONF_EMPTY 3 /* L-0.1 -> L-0 | ||
29 | L-1.1 -> L-0 */ | ||
30 | #define EV_LISTEN_CONF_OK 4 /* L-0.1 -> L-1 | ||
31 | L-1.1 -> L.1 */ | ||
32 | |||
33 | /* | ||
34 | * per plci state machine | ||
35 | */ | ||
36 | #define ST_PLCI_NONE 0 /* P-0 */ | ||
37 | #define ST_PLCI_OUTGOING 1 /* P-0.1 */ | ||
38 | #define ST_PLCI_ALLOCATED 2 /* P-1 */ | ||
39 | #define ST_PLCI_ACTIVE 3 /* P-ACT */ | ||
40 | #define ST_PLCI_INCOMING 4 /* P-2 */ | ||
41 | #define ST_PLCI_FACILITY_IND 5 /* P-3 */ | ||
42 | #define ST_PLCI_ACCEPTING 6 /* P-4 */ | ||
43 | #define ST_PLCI_DISCONNECTING 7 /* P-5 */ | ||
44 | #define ST_PLCI_DISCONNECTED 8 /* P-6 */ | ||
45 | #define ST_PLCI_RESUMEING 9 /* P-0.Res */ | ||
46 | #define ST_PLCI_RESUME 10 /* P-Res */ | ||
47 | #define ST_PLCI_HELD 11 /* P-HELD */ | ||
48 | |||
49 | #define EV_PLCI_CONNECT_REQ 1 /* P-0 -> P-0.1 | ||
50 | */ | ||
51 | #define EV_PLCI_CONNECT_CONF_ERROR 2 /* P-0.1 -> P-0 | ||
52 | */ | ||
53 | #define EV_PLCI_CONNECT_CONF_OK 3 /* P-0.1 -> P-1 | ||
54 | */ | ||
55 | #define EV_PLCI_FACILITY_IND_UP 4 /* P-0 -> P-1 | ||
56 | */ | ||
57 | #define EV_PLCI_CONNECT_IND 5 /* P-0 -> P-2 | ||
58 | */ | ||
59 | #define EV_PLCI_CONNECT_ACTIVE_IND 6 /* P-1 -> P-ACT | ||
60 | */ | ||
61 | #define EV_PLCI_CONNECT_REJECT 7 /* P-2 -> P-5 | ||
62 | P-3 -> P-5 | ||
63 | */ | ||
64 | #define EV_PLCI_DISCONNECT_REQ 8 /* P-1 -> P-5 | ||
65 | P-2 -> P-5 | ||
66 | P-3 -> P-5 | ||
67 | P-4 -> P-5 | ||
68 | P-ACT -> P-5 | ||
69 | P-Res -> P-5 (*) | ||
70 | P-HELD -> P-5 (*) | ||
71 | */ | ||
72 | #define EV_PLCI_DISCONNECT_IND 9 /* P-1 -> P-6 | ||
73 | P-2 -> P-6 | ||
74 | P-3 -> P-6 | ||
75 | P-4 -> P-6 | ||
76 | P-5 -> P-6 | ||
77 | P-ACT -> P-6 | ||
78 | P-Res -> P-6 (*) | ||
79 | P-HELD -> P-6 (*) | ||
80 | */ | ||
81 | #define EV_PLCI_FACILITY_IND_DOWN 10 /* P-0.1 -> P-5 | ||
82 | P-1 -> P-5 | ||
83 | P-ACT -> P-5 | ||
84 | P-2 -> P-5 | ||
85 | P-3 -> P-5 | ||
86 | P-4 -> P-5 | ||
87 | */ | ||
88 | #define EV_PLCI_DISCONNECT_RESP 11 /* P-6 -> P-0 | ||
89 | */ | ||
90 | #define EV_PLCI_CONNECT_RESP 12 /* P-6 -> P-0 | ||
91 | */ | ||
92 | |||
93 | #define EV_PLCI_RESUME_REQ 13 /* P-0 -> P-0.Res | ||
94 | */ | ||
95 | #define EV_PLCI_RESUME_CONF_OK 14 /* P-0.Res -> P-Res | ||
96 | */ | ||
97 | #define EV_PLCI_RESUME_CONF_ERROR 15 /* P-0.Res -> P-0 | ||
98 | */ | ||
99 | #define EV_PLCI_RESUME_IND 16 /* P-Res -> P-ACT | ||
100 | */ | ||
101 | #define EV_PLCI_HOLD_IND 17 /* P-ACT -> P-HELD | ||
102 | */ | ||
103 | #define EV_PLCI_RETRIEVE_IND 18 /* P-HELD -> P-ACT | ||
104 | */ | ||
105 | #define EV_PLCI_SUSPEND_IND 19 /* P-ACT -> P-5 | ||
106 | */ | ||
107 | #define EV_PLCI_CD_IND 20 /* P-2 -> P-5 | ||
108 | */ | ||
109 | |||
110 | /* | ||
111 | * per ncci state machine | ||
112 | */ | ||
113 | #define ST_NCCI_PREVIOUS -1 | ||
114 | #define ST_NCCI_NONE 0 /* N-0 */ | ||
115 | #define ST_NCCI_OUTGOING 1 /* N-0.1 */ | ||
116 | #define ST_NCCI_INCOMING 2 /* N-1 */ | ||
117 | #define ST_NCCI_ALLOCATED 3 /* N-2 */ | ||
118 | #define ST_NCCI_ACTIVE 4 /* N-ACT */ | ||
119 | #define ST_NCCI_RESETING 5 /* N-3 */ | ||
120 | #define ST_NCCI_DISCONNECTING 6 /* N-4 */ | ||
121 | #define ST_NCCI_DISCONNECTED 7 /* N-5 */ | ||
122 | |||
123 | #define EV_NCCI_CONNECT_B3_REQ 1 /* N-0 -> N-0.1 */ | ||
124 | #define EV_NCCI_CONNECT_B3_IND 2 /* N-0 -> N.1 */ | ||
125 | #define EV_NCCI_CONNECT_B3_CONF_OK 3 /* N-0.1 -> N.2 */ | ||
126 | #define EV_NCCI_CONNECT_B3_CONF_ERROR 4 /* N-0.1 -> N.0 */ | ||
127 | #define EV_NCCI_CONNECT_B3_REJECT 5 /* N-1 -> N-4 */ | ||
128 | #define EV_NCCI_CONNECT_B3_RESP 6 /* N-1 -> N-2 */ | ||
129 | #define EV_NCCI_CONNECT_B3_ACTIVE_IND 7 /* N-2 -> N-ACT */ | ||
130 | #define EV_NCCI_RESET_B3_REQ 8 /* N-ACT -> N-3 */ | ||
131 | #define EV_NCCI_RESET_B3_IND 9 /* N-3 -> N-ACT */ | ||
132 | #define EV_NCCI_DISCONNECT_B3_IND 10 /* N-4 -> N.5 */ | ||
133 | #define EV_NCCI_DISCONNECT_B3_CONF_ERROR 11 /* N-4 -> previous */ | ||
134 | #define EV_NCCI_DISCONNECT_B3_REQ 12 /* N-1 -> N-4 | ||
135 | N-2 -> N-4 | ||
136 | N-3 -> N-4 | ||
137 | N-ACT -> N-4 */ | ||
138 | #define EV_NCCI_DISCONNECT_B3_RESP 13 /* N-5 -> N-0 */ | ||
139 | |||
140 | #endif /* __CAPIDRV_H__ */ | ||
diff --git a/drivers/isdn/divert/Makefile b/drivers/isdn/divert/Makefile deleted file mode 100644 index 07684fe53537..000000000000 --- a/drivers/isdn/divert/Makefile +++ /dev/null | |||
@@ -1,10 +0,0 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0-only | ||
2 | # Makefile for the dss1_divert ISDN module | ||
3 | |||
4 | # Each configuration option enables a list of files. | ||
5 | |||
6 | obj-$(CONFIG_ISDN_DIVERSION) += dss1_divert.o | ||
7 | |||
8 | # Multipart objects. | ||
9 | |||
10 | dss1_divert-y := isdn_divert.o divert_procfs.o divert_init.o | ||
diff --git a/drivers/isdn/divert/divert_init.c b/drivers/isdn/divert/divert_init.c deleted file mode 100644 index 267dede13bfd..000000000000 --- a/drivers/isdn/divert/divert_init.c +++ /dev/null | |||
@@ -1,82 +0,0 @@ | |||
1 | /* $Id divert_init.c,v 1.5.6.2 2001/01/24 22:18:17 kai Exp $ | ||
2 | * | ||
3 | * Module init for DSS1 diversion services for i4l. | ||
4 | * | ||
5 | * Copyright 1999 by Werner Cornelius (werner@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 | #include <linux/module.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/kernel.h> | ||
15 | |||
16 | #include "isdn_divert.h" | ||
17 | |||
18 | MODULE_DESCRIPTION("ISDN4Linux: Call diversion support"); | ||
19 | MODULE_AUTHOR("Werner Cornelius"); | ||
20 | MODULE_LICENSE("GPL"); | ||
21 | |||
22 | /****************************************/ | ||
23 | /* structure containing interface to hl */ | ||
24 | /****************************************/ | ||
25 | isdn_divert_if divert_if = { | ||
26 | DIVERT_IF_MAGIC, /* magic value */ | ||
27 | DIVERT_CMD_REG, /* register cmd */ | ||
28 | ll_callback, /* callback routine from ll */ | ||
29 | NULL, /* command still not specified */ | ||
30 | NULL, /* drv_to_name */ | ||
31 | NULL, /* name_to_drv */ | ||
32 | }; | ||
33 | |||
34 | /*************************/ | ||
35 | /* Module interface code */ | ||
36 | /* no cmd line parms */ | ||
37 | /*************************/ | ||
38 | static int __init divert_init(void) | ||
39 | { | ||
40 | int i; | ||
41 | |||
42 | if (divert_dev_init()) { | ||
43 | printk(KERN_WARNING "dss1_divert: cannot install device, not loaded\n"); | ||
44 | return (-EIO); | ||
45 | } | ||
46 | if ((i = DIVERT_REG_NAME(&divert_if)) != DIVERT_NO_ERR) { | ||
47 | divert_dev_deinit(); | ||
48 | printk(KERN_WARNING "dss1_divert: error %d registering module, not loaded\n", i); | ||
49 | return (-EIO); | ||
50 | } | ||
51 | printk(KERN_INFO "dss1_divert module successfully installed\n"); | ||
52 | return (0); | ||
53 | } | ||
54 | |||
55 | /**********************/ | ||
56 | /* Module deinit code */ | ||
57 | /**********************/ | ||
58 | static void __exit divert_exit(void) | ||
59 | { | ||
60 | unsigned long flags; | ||
61 | int i; | ||
62 | |||
63 | spin_lock_irqsave(&divert_lock, flags); | ||
64 | divert_if.cmd = DIVERT_CMD_REL; /* release */ | ||
65 | if ((i = DIVERT_REG_NAME(&divert_if)) != DIVERT_NO_ERR) { | ||
66 | printk(KERN_WARNING "dss1_divert: error %d releasing module\n", i); | ||
67 | spin_unlock_irqrestore(&divert_lock, flags); | ||
68 | return; | ||
69 | } | ||
70 | if (divert_dev_deinit()) { | ||
71 | printk(KERN_WARNING "dss1_divert: device busy, remove cancelled\n"); | ||
72 | spin_unlock_irqrestore(&divert_lock, flags); | ||
73 | return; | ||
74 | } | ||
75 | spin_unlock_irqrestore(&divert_lock, flags); | ||
76 | deleterule(-1); /* delete all rules and free mem */ | ||
77 | deleteprocs(); | ||
78 | printk(KERN_INFO "dss1_divert module successfully removed \n"); | ||
79 | } | ||
80 | |||
81 | module_init(divert_init); | ||
82 | module_exit(divert_exit); | ||
diff --git a/drivers/isdn/divert/divert_procfs.c b/drivers/isdn/divert/divert_procfs.c deleted file mode 100644 index 342585e04fd3..000000000000 --- a/drivers/isdn/divert/divert_procfs.c +++ /dev/null | |||
@@ -1,336 +0,0 @@ | |||
1 | /* $Id: divert_procfs.c,v 1.11.6.2 2001/09/23 22:24:36 kai Exp $ | ||
2 | * | ||
3 | * Filesystem handling for the diversion supplementary services. | ||
4 | * | ||
5 | * Copyright 1998 by Werner Cornelius (werner@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 | #include <linux/module.h> | ||
13 | #include <linux/poll.h> | ||
14 | #include <linux/slab.h> | ||
15 | #ifdef CONFIG_PROC_FS | ||
16 | #include <linux/proc_fs.h> | ||
17 | #else | ||
18 | #include <linux/fs.h> | ||
19 | #endif | ||
20 | #include <linux/sched.h> | ||
21 | #include <linux/isdnif.h> | ||
22 | #include <net/net_namespace.h> | ||
23 | #include <linux/mutex.h> | ||
24 | #include "isdn_divert.h" | ||
25 | |||
26 | |||
27 | /*********************************/ | ||
28 | /* Variables for interface queue */ | ||
29 | /*********************************/ | ||
30 | ulong if_used = 0; /* number of interface users */ | ||
31 | static DEFINE_MUTEX(isdn_divert_mutex); | ||
32 | static struct divert_info *divert_info_head = NULL; /* head of queue */ | ||
33 | static struct divert_info *divert_info_tail = NULL; /* pointer to last entry */ | ||
34 | static DEFINE_SPINLOCK(divert_info_lock);/* lock for queue */ | ||
35 | static wait_queue_head_t rd_queue; | ||
36 | |||
37 | /*********************************/ | ||
38 | /* put an info buffer into queue */ | ||
39 | /*********************************/ | ||
40 | void | ||
41 | put_info_buffer(char *cp) | ||
42 | { | ||
43 | struct divert_info *ib; | ||
44 | unsigned long flags; | ||
45 | |||
46 | if (if_used <= 0) | ||
47 | return; | ||
48 | if (!cp) | ||
49 | return; | ||
50 | if (!*cp) | ||
51 | return; | ||
52 | if (!(ib = kmalloc(sizeof(struct divert_info) + strlen(cp), GFP_ATOMIC))) | ||
53 | return; /* no memory */ | ||
54 | strcpy(ib->info_start, cp); /* set output string */ | ||
55 | ib->next = NULL; | ||
56 | spin_lock_irqsave(&divert_info_lock, flags); | ||
57 | ib->usage_cnt = if_used; | ||
58 | if (!divert_info_head) | ||
59 | divert_info_head = ib; /* new head */ | ||
60 | else | ||
61 | divert_info_tail->next = ib; /* follows existing messages */ | ||
62 | divert_info_tail = ib; /* new tail */ | ||
63 | |||
64 | /* delete old entrys */ | ||
65 | while (divert_info_head->next) { | ||
66 | if ((divert_info_head->usage_cnt <= 0) && | ||
67 | (divert_info_head->next->usage_cnt <= 0)) { | ||
68 | ib = divert_info_head; | ||
69 | divert_info_head = divert_info_head->next; | ||
70 | kfree(ib); | ||
71 | } else | ||
72 | break; | ||
73 | } /* divert_info_head->next */ | ||
74 | spin_unlock_irqrestore(&divert_info_lock, flags); | ||
75 | wake_up_interruptible(&(rd_queue)); | ||
76 | } /* put_info_buffer */ | ||
77 | |||
78 | #ifdef CONFIG_PROC_FS | ||
79 | |||
80 | /**********************************/ | ||
81 | /* deflection device read routine */ | ||
82 | /**********************************/ | ||
83 | static ssize_t | ||
84 | isdn_divert_read(struct file *file, char __user *buf, size_t count, loff_t *off) | ||
85 | { | ||
86 | struct divert_info *inf; | ||
87 | int len; | ||
88 | |||
89 | if (!(inf = *((struct divert_info **) file->private_data))) { | ||
90 | if (file->f_flags & O_NONBLOCK) | ||
91 | return -EAGAIN; | ||
92 | wait_event_interruptible(rd_queue, (inf = | ||
93 | *((struct divert_info **) file->private_data))); | ||
94 | } | ||
95 | if (!inf) | ||
96 | return (0); | ||
97 | |||
98 | inf->usage_cnt--; /* new usage count */ | ||
99 | file->private_data = &inf->next; /* next structure */ | ||
100 | if ((len = strlen(inf->info_start)) <= count) { | ||
101 | if (copy_to_user(buf, inf->info_start, len)) | ||
102 | return -EFAULT; | ||
103 | *off += len; | ||
104 | return (len); | ||
105 | } | ||
106 | return (0); | ||
107 | } /* isdn_divert_read */ | ||
108 | |||
109 | /**********************************/ | ||
110 | /* deflection device write routine */ | ||
111 | /**********************************/ | ||
112 | static ssize_t | ||
113 | isdn_divert_write(struct file *file, const char __user *buf, size_t count, loff_t *off) | ||
114 | { | ||
115 | return (-ENODEV); | ||
116 | } /* isdn_divert_write */ | ||
117 | |||
118 | |||
119 | /***************************************/ | ||
120 | /* select routines for various kernels */ | ||
121 | /***************************************/ | ||
122 | static __poll_t | ||
123 | isdn_divert_poll(struct file *file, poll_table *wait) | ||
124 | { | ||
125 | __poll_t mask = 0; | ||
126 | |||
127 | poll_wait(file, &(rd_queue), wait); | ||
128 | /* mask = EPOLLOUT | EPOLLWRNORM; */ | ||
129 | if (*((struct divert_info **) file->private_data)) { | ||
130 | mask |= EPOLLIN | EPOLLRDNORM; | ||
131 | } | ||
132 | return mask; | ||
133 | } /* isdn_divert_poll */ | ||
134 | |||
135 | /****************/ | ||
136 | /* Open routine */ | ||
137 | /****************/ | ||
138 | static int | ||
139 | isdn_divert_open(struct inode *ino, struct file *filep) | ||
140 | { | ||
141 | unsigned long flags; | ||
142 | |||
143 | spin_lock_irqsave(&divert_info_lock, flags); | ||
144 | if_used++; | ||
145 | if (divert_info_head) | ||
146 | filep->private_data = &(divert_info_tail->next); | ||
147 | else | ||
148 | filep->private_data = &divert_info_head; | ||
149 | spin_unlock_irqrestore(&divert_info_lock, flags); | ||
150 | /* start_divert(); */ | ||
151 | return nonseekable_open(ino, filep); | ||
152 | } /* isdn_divert_open */ | ||
153 | |||
154 | /*******************/ | ||
155 | /* close routine */ | ||
156 | /*******************/ | ||
157 | static int | ||
158 | isdn_divert_close(struct inode *ino, struct file *filep) | ||
159 | { | ||
160 | struct divert_info *inf; | ||
161 | unsigned long flags; | ||
162 | |||
163 | spin_lock_irqsave(&divert_info_lock, flags); | ||
164 | if_used--; | ||
165 | inf = *((struct divert_info **) filep->private_data); | ||
166 | while (inf) { | ||
167 | inf->usage_cnt--; | ||
168 | inf = inf->next; | ||
169 | } | ||
170 | if (if_used <= 0) | ||
171 | while (divert_info_head) { | ||
172 | inf = divert_info_head; | ||
173 | divert_info_head = divert_info_head->next; | ||
174 | kfree(inf); | ||
175 | } | ||
176 | spin_unlock_irqrestore(&divert_info_lock, flags); | ||
177 | return (0); | ||
178 | } /* isdn_divert_close */ | ||
179 | |||
180 | /*********/ | ||
181 | /* IOCTL */ | ||
182 | /*********/ | ||
183 | static int isdn_divert_ioctl_unlocked(struct file *file, uint cmd, ulong arg) | ||
184 | { | ||
185 | divert_ioctl dioctl; | ||
186 | int i; | ||
187 | unsigned long flags; | ||
188 | divert_rule *rulep; | ||
189 | char *cp; | ||
190 | |||
191 | if (copy_from_user(&dioctl, (void __user *) arg, sizeof(dioctl))) | ||
192 | return -EFAULT; | ||
193 | |||
194 | switch (cmd) { | ||
195 | case IIOCGETVER: | ||
196 | dioctl.drv_version = DIVERT_IIOC_VERSION; /* set version */ | ||
197 | break; | ||
198 | |||
199 | case IIOCGETDRV: | ||
200 | if ((dioctl.getid.drvid = divert_if.name_to_drv(dioctl.getid.drvnam)) < 0) | ||
201 | return (-EINVAL); | ||
202 | break; | ||
203 | |||
204 | case IIOCGETNAM: | ||
205 | cp = divert_if.drv_to_name(dioctl.getid.drvid); | ||
206 | if (!cp) | ||
207 | return (-EINVAL); | ||
208 | if (!*cp) | ||
209 | return (-EINVAL); | ||
210 | strcpy(dioctl.getid.drvnam, cp); | ||
211 | break; | ||
212 | |||
213 | case IIOCGETRULE: | ||
214 | if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx))) | ||
215 | return (-EINVAL); | ||
216 | dioctl.getsetrule.rule = *rulep; /* copy data */ | ||
217 | break; | ||
218 | |||
219 | case IIOCMODRULE: | ||
220 | if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx))) | ||
221 | return (-EINVAL); | ||
222 | spin_lock_irqsave(&divert_lock, flags); | ||
223 | *rulep = dioctl.getsetrule.rule; /* copy data */ | ||
224 | spin_unlock_irqrestore(&divert_lock, flags); | ||
225 | return (0); /* no copy required */ | ||
226 | break; | ||
227 | |||
228 | case IIOCINSRULE: | ||
229 | return (insertrule(dioctl.getsetrule.ruleidx, &dioctl.getsetrule.rule)); | ||
230 | break; | ||
231 | |||
232 | case IIOCDELRULE: | ||
233 | return (deleterule(dioctl.getsetrule.ruleidx)); | ||
234 | break; | ||
235 | |||
236 | case IIOCDODFACT: | ||
237 | return (deflect_extern_action(dioctl.fwd_ctrl.subcmd, | ||
238 | dioctl.fwd_ctrl.callid, | ||
239 | dioctl.fwd_ctrl.to_nr)); | ||
240 | |||
241 | case IIOCDOCFACT: | ||
242 | case IIOCDOCFDIS: | ||
243 | case IIOCDOCFINT: | ||
244 | if (!divert_if.drv_to_name(dioctl.cf_ctrl.drvid)) | ||
245 | return (-EINVAL); /* invalid driver */ | ||
246 | if (strnlen(dioctl.cf_ctrl.msn, sizeof(dioctl.cf_ctrl.msn)) == | ||
247 | sizeof(dioctl.cf_ctrl.msn)) | ||
248 | return -EINVAL; | ||
249 | if (strnlen(dioctl.cf_ctrl.fwd_nr, sizeof(dioctl.cf_ctrl.fwd_nr)) == | ||
250 | sizeof(dioctl.cf_ctrl.fwd_nr)) | ||
251 | return -EINVAL; | ||
252 | if ((i = cf_command(dioctl.cf_ctrl.drvid, | ||
253 | (cmd == IIOCDOCFACT) ? 1 : (cmd == IIOCDOCFDIS) ? 0 : 2, | ||
254 | dioctl.cf_ctrl.cfproc, | ||
255 | dioctl.cf_ctrl.msn, | ||
256 | dioctl.cf_ctrl.service, | ||
257 | dioctl.cf_ctrl.fwd_nr, | ||
258 | &dioctl.cf_ctrl.procid))) | ||
259 | return (i); | ||
260 | break; | ||
261 | |||
262 | default: | ||
263 | return (-EINVAL); | ||
264 | } /* switch cmd */ | ||
265 | return copy_to_user((void __user *)arg, &dioctl, sizeof(dioctl)) ? -EFAULT : 0; | ||
266 | } /* isdn_divert_ioctl */ | ||
267 | |||
268 | static long isdn_divert_ioctl(struct file *file, uint cmd, ulong arg) | ||
269 | { | ||
270 | long ret; | ||
271 | |||
272 | mutex_lock(&isdn_divert_mutex); | ||
273 | ret = isdn_divert_ioctl_unlocked(file, cmd, arg); | ||
274 | mutex_unlock(&isdn_divert_mutex); | ||
275 | |||
276 | return ret; | ||
277 | } | ||
278 | |||
279 | static const struct file_operations isdn_fops = | ||
280 | { | ||
281 | .owner = THIS_MODULE, | ||
282 | .llseek = no_llseek, | ||
283 | .read = isdn_divert_read, | ||
284 | .write = isdn_divert_write, | ||
285 | .poll = isdn_divert_poll, | ||
286 | .unlocked_ioctl = isdn_divert_ioctl, | ||
287 | .open = isdn_divert_open, | ||
288 | .release = isdn_divert_close, | ||
289 | }; | ||
290 | |||
291 | /****************************/ | ||
292 | /* isdn subdir in /proc/net */ | ||
293 | /****************************/ | ||
294 | static struct proc_dir_entry *isdn_proc_entry = NULL; | ||
295 | static struct proc_dir_entry *isdn_divert_entry = NULL; | ||
296 | #endif /* CONFIG_PROC_FS */ | ||
297 | |||
298 | /***************************************************************************/ | ||
299 | /* divert_dev_init must be called before the proc filesystem may be used */ | ||
300 | /***************************************************************************/ | ||
301 | int | ||
302 | divert_dev_init(void) | ||
303 | { | ||
304 | |||
305 | init_waitqueue_head(&rd_queue); | ||
306 | |||
307 | #ifdef CONFIG_PROC_FS | ||
308 | isdn_proc_entry = proc_mkdir("isdn", init_net.proc_net); | ||
309 | if (!isdn_proc_entry) | ||
310 | return (-1); | ||
311 | isdn_divert_entry = proc_create("divert", S_IFREG | S_IRUGO, | ||
312 | isdn_proc_entry, &isdn_fops); | ||
313 | if (!isdn_divert_entry) { | ||
314 | remove_proc_entry("isdn", init_net.proc_net); | ||
315 | return (-1); | ||
316 | } | ||
317 | #endif /* CONFIG_PROC_FS */ | ||
318 | |||
319 | return (0); | ||
320 | } /* divert_dev_init */ | ||
321 | |||
322 | /***************************************************************************/ | ||
323 | /* divert_dev_deinit must be called before leaving isdn when included as */ | ||
324 | /* a module. */ | ||
325 | /***************************************************************************/ | ||
326 | int | ||
327 | divert_dev_deinit(void) | ||
328 | { | ||
329 | |||
330 | #ifdef CONFIG_PROC_FS | ||
331 | remove_proc_entry("divert", isdn_proc_entry); | ||
332 | remove_proc_entry("isdn", init_net.proc_net); | ||
333 | #endif /* CONFIG_PROC_FS */ | ||
334 | |||
335 | return (0); | ||
336 | } /* divert_dev_deinit */ | ||
diff --git a/drivers/isdn/divert/isdn_divert.c b/drivers/isdn/divert/isdn_divert.c deleted file mode 100644 index 5620fd2c6009..000000000000 --- a/drivers/isdn/divert/isdn_divert.c +++ /dev/null | |||
@@ -1,846 +0,0 @@ | |||
1 | /* $Id: isdn_divert.c,v 1.6.6.3 2001/09/23 22:24:36 kai Exp $ | ||
2 | * | ||
3 | * DSS1 main diversion supplementary handling for i4l. | ||
4 | * | ||
5 | * Copyright 1999 by Werner Cornelius (werner@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 | #include <linux/proc_fs.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/timer.h> | ||
15 | #include <linux/jiffies.h> | ||
16 | |||
17 | #include "isdn_divert.h" | ||
18 | |||
19 | /**********************************/ | ||
20 | /* structure keeping calling info */ | ||
21 | /**********************************/ | ||
22 | struct call_struc { | ||
23 | isdn_ctrl ics; /* delivered setup + driver parameters */ | ||
24 | ulong divert_id; /* Id delivered to user */ | ||
25 | unsigned char akt_state; /* actual state */ | ||
26 | char deflect_dest[35]; /* deflection destination */ | ||
27 | struct timer_list timer; /* timer control structure */ | ||
28 | char info[90]; /* device info output */ | ||
29 | struct call_struc *next; /* pointer to next entry */ | ||
30 | struct call_struc *prev; | ||
31 | }; | ||
32 | |||
33 | |||
34 | /********************************************/ | ||
35 | /* structure keeping deflection table entry */ | ||
36 | /********************************************/ | ||
37 | struct deflect_struc { | ||
38 | struct deflect_struc *next, *prev; | ||
39 | divert_rule rule; /* used rule */ | ||
40 | }; | ||
41 | |||
42 | |||
43 | /*****************************************/ | ||
44 | /* variables for main diversion services */ | ||
45 | /*****************************************/ | ||
46 | /* diversion/deflection processes */ | ||
47 | static struct call_struc *divert_head = NULL; /* head of remembered entrys */ | ||
48 | static ulong next_id = 1; /* next info id */ | ||
49 | static struct deflect_struc *table_head = NULL; | ||
50 | static struct deflect_struc *table_tail = NULL; | ||
51 | static unsigned char extern_wait_max = 4; /* maximum wait in s for external process */ | ||
52 | |||
53 | DEFINE_SPINLOCK(divert_lock); | ||
54 | |||
55 | /***************************/ | ||
56 | /* timer callback function */ | ||
57 | /***************************/ | ||
58 | static void deflect_timer_expire(struct timer_list *t) | ||
59 | { | ||
60 | unsigned long flags; | ||
61 | struct call_struc *cs = from_timer(cs, t, timer); | ||
62 | |||
63 | spin_lock_irqsave(&divert_lock, flags); | ||
64 | del_timer(&cs->timer); /* delete active timer */ | ||
65 | spin_unlock_irqrestore(&divert_lock, flags); | ||
66 | |||
67 | switch (cs->akt_state) { | ||
68 | case DEFLECT_PROCEED: | ||
69 | cs->ics.command = ISDN_CMD_HANGUP; /* cancel action */ | ||
70 | divert_if.ll_cmd(&cs->ics); | ||
71 | spin_lock_irqsave(&divert_lock, flags); | ||
72 | cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ | ||
73 | cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); | ||
74 | add_timer(&cs->timer); | ||
75 | spin_unlock_irqrestore(&divert_lock, flags); | ||
76 | break; | ||
77 | |||
78 | case DEFLECT_ALERT: | ||
79 | cs->ics.command = ISDN_CMD_REDIR; /* protocol */ | ||
80 | strlcpy(cs->ics.parm.setup.phone, cs->deflect_dest, sizeof(cs->ics.parm.setup.phone)); | ||
81 | strcpy(cs->ics.parm.setup.eazmsn, "Testtext delayed"); | ||
82 | divert_if.ll_cmd(&cs->ics); | ||
83 | spin_lock_irqsave(&divert_lock, flags); | ||
84 | cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ | ||
85 | cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); | ||
86 | add_timer(&cs->timer); | ||
87 | spin_unlock_irqrestore(&divert_lock, flags); | ||
88 | break; | ||
89 | |||
90 | case DEFLECT_AUTODEL: | ||
91 | default: | ||
92 | spin_lock_irqsave(&divert_lock, flags); | ||
93 | if (cs->prev) | ||
94 | cs->prev->next = cs->next; /* forward link */ | ||
95 | else | ||
96 | divert_head = cs->next; | ||
97 | if (cs->next) | ||
98 | cs->next->prev = cs->prev; /* back link */ | ||
99 | spin_unlock_irqrestore(&divert_lock, flags); | ||
100 | kfree(cs); | ||
101 | return; | ||
102 | |||
103 | } /* switch */ | ||
104 | } /* deflect_timer_func */ | ||
105 | |||
106 | |||
107 | /*****************************************/ | ||
108 | /* handle call forwarding de/activations */ | ||
109 | /* 0 = deact, 1 = act, 2 = interrogate */ | ||
110 | /*****************************************/ | ||
111 | int cf_command(int drvid, int mode, | ||
112 | u_char proc, char *msn, | ||
113 | u_char service, char *fwd_nr, ulong *procid) | ||
114 | { | ||
115 | unsigned long flags; | ||
116 | int retval, msnlen; | ||
117 | int fwd_len; | ||
118 | char *p, *ielenp, tmp[60]; | ||
119 | struct call_struc *cs; | ||
120 | |||
121 | if (strchr(msn, '.')) return (-EINVAL); /* subaddress not allowed in msn */ | ||
122 | if ((proc & 0x7F) > 2) return (-EINVAL); | ||
123 | proc &= 3; | ||
124 | p = tmp; | ||
125 | *p++ = 0x30; /* enumeration */ | ||
126 | ielenp = p++; /* remember total length position */ | ||
127 | *p++ = 0xa; /* proc tag */ | ||
128 | *p++ = 1; /* length */ | ||
129 | *p++ = proc & 0x7F; /* procedure to de/activate/interrogate */ | ||
130 | *p++ = 0xa; /* service tag */ | ||
131 | *p++ = 1; /* length */ | ||
132 | *p++ = service; /* service to handle */ | ||
133 | |||
134 | if (mode == 1) { | ||
135 | if (!*fwd_nr) return (-EINVAL); /* destination missing */ | ||
136 | if (strchr(fwd_nr, '.')) return (-EINVAL); /* subaddress not allowed */ | ||
137 | fwd_len = strlen(fwd_nr); | ||
138 | *p++ = 0x30; /* number enumeration */ | ||
139 | *p++ = fwd_len + 2; /* complete forward to len */ | ||
140 | *p++ = 0x80; /* fwd to nr */ | ||
141 | *p++ = fwd_len; /* length of number */ | ||
142 | strcpy(p, fwd_nr); /* copy number */ | ||
143 | p += fwd_len; /* pointer beyond fwd */ | ||
144 | } /* activate */ | ||
145 | |||
146 | msnlen = strlen(msn); | ||
147 | *p++ = 0x80; /* msn number */ | ||
148 | if (msnlen > 1) { | ||
149 | *p++ = msnlen; /* length */ | ||
150 | strcpy(p, msn); | ||
151 | p += msnlen; | ||
152 | } else | ||
153 | *p++ = 0; | ||
154 | |||
155 | *ielenp = p - ielenp - 1; /* set total IE length */ | ||
156 | |||
157 | /* allocate mem for information struct */ | ||
158 | if (!(cs = kmalloc(sizeof(struct call_struc), GFP_ATOMIC))) | ||
159 | return (-ENOMEM); /* no memory */ | ||
160 | timer_setup(&cs->timer, deflect_timer_expire, 0); | ||
161 | cs->info[0] = '\0'; | ||
162 | cs->ics.driver = drvid; | ||
163 | cs->ics.command = ISDN_CMD_PROT_IO; /* protocol specific io */ | ||
164 | cs->ics.arg = DSS1_CMD_INVOKE; /* invoke supplementary service */ | ||
165 | cs->ics.parm.dss1_io.proc = (mode == 1) ? 7 : (mode == 2) ? 11 : 8; /* operation */ | ||
166 | cs->ics.parm.dss1_io.timeout = 4000; /* from ETS 300 207-1 */ | ||
167 | cs->ics.parm.dss1_io.datalen = p - tmp; /* total len */ | ||
168 | cs->ics.parm.dss1_io.data = tmp; /* start of buffer */ | ||
169 | |||
170 | spin_lock_irqsave(&divert_lock, flags); | ||
171 | cs->ics.parm.dss1_io.ll_id = next_id++; /* id for callback */ | ||
172 | spin_unlock_irqrestore(&divert_lock, flags); | ||
173 | *procid = cs->ics.parm.dss1_io.ll_id; | ||
174 | |||
175 | sprintf(cs->info, "%d 0x%lx %s%s 0 %s %02x %d%s%s\n", | ||
176 | (!mode) ? DIVERT_DEACTIVATE : (mode == 1) ? DIVERT_ACTIVATE : DIVERT_REPORT, | ||
177 | cs->ics.parm.dss1_io.ll_id, | ||
178 | (mode != 2) ? "" : "0 ", | ||
179 | divert_if.drv_to_name(cs->ics.driver), | ||
180 | msn, | ||
181 | service & 0xFF, | ||
182 | proc, | ||
183 | (mode != 1) ? "" : " 0 ", | ||
184 | (mode != 1) ? "" : fwd_nr); | ||
185 | |||
186 | retval = divert_if.ll_cmd(&cs->ics); /* execute command */ | ||
187 | |||
188 | if (!retval) { | ||
189 | cs->prev = NULL; | ||
190 | spin_lock_irqsave(&divert_lock, flags); | ||
191 | cs->next = divert_head; | ||
192 | divert_head = cs; | ||
193 | spin_unlock_irqrestore(&divert_lock, flags); | ||
194 | } else | ||
195 | kfree(cs); | ||
196 | return (retval); | ||
197 | } /* cf_command */ | ||
198 | |||
199 | |||
200 | /****************************************/ | ||
201 | /* handle a external deflection command */ | ||
202 | /****************************************/ | ||
203 | int deflect_extern_action(u_char cmd, ulong callid, char *to_nr) | ||
204 | { | ||
205 | struct call_struc *cs; | ||
206 | isdn_ctrl ic; | ||
207 | unsigned long flags; | ||
208 | int i; | ||
209 | |||
210 | if ((cmd & 0x7F) > 2) return (-EINVAL); /* invalid command */ | ||
211 | cs = divert_head; /* start of parameter list */ | ||
212 | while (cs) { | ||
213 | if (cs->divert_id == callid) break; /* found */ | ||
214 | cs = cs->next; | ||
215 | } /* search entry */ | ||
216 | if (!cs) return (-EINVAL); /* invalid callid */ | ||
217 | |||
218 | ic.driver = cs->ics.driver; | ||
219 | ic.arg = cs->ics.arg; | ||
220 | i = -EINVAL; | ||
221 | if (cs->akt_state == DEFLECT_AUTODEL) return (i); /* no valid call */ | ||
222 | switch (cmd & 0x7F) { | ||
223 | case 0: /* hangup */ | ||
224 | del_timer(&cs->timer); | ||
225 | ic.command = ISDN_CMD_HANGUP; | ||
226 | i = divert_if.ll_cmd(&ic); | ||
227 | spin_lock_irqsave(&divert_lock, flags); | ||
228 | cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ | ||
229 | cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); | ||
230 | add_timer(&cs->timer); | ||
231 | spin_unlock_irqrestore(&divert_lock, flags); | ||
232 | break; | ||
233 | |||
234 | case 1: /* alert */ | ||
235 | if (cs->akt_state == DEFLECT_ALERT) return (0); | ||
236 | cmd &= 0x7F; /* never wait */ | ||
237 | del_timer(&cs->timer); | ||
238 | ic.command = ISDN_CMD_ALERT; | ||
239 | if ((i = divert_if.ll_cmd(&ic))) { | ||
240 | spin_lock_irqsave(&divert_lock, flags); | ||
241 | cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ | ||
242 | cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); | ||
243 | add_timer(&cs->timer); | ||
244 | spin_unlock_irqrestore(&divert_lock, flags); | ||
245 | } else | ||
246 | cs->akt_state = DEFLECT_ALERT; | ||
247 | break; | ||
248 | |||
249 | case 2: /* redir */ | ||
250 | del_timer(&cs->timer); | ||
251 | strlcpy(cs->ics.parm.setup.phone, to_nr, sizeof(cs->ics.parm.setup.phone)); | ||
252 | strcpy(cs->ics.parm.setup.eazmsn, "Testtext manual"); | ||
253 | ic.command = ISDN_CMD_REDIR; | ||
254 | if ((i = divert_if.ll_cmd(&ic))) { | ||
255 | spin_lock_irqsave(&divert_lock, flags); | ||
256 | cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ | ||
257 | cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); | ||
258 | add_timer(&cs->timer); | ||
259 | spin_unlock_irqrestore(&divert_lock, flags); | ||
260 | } else | ||
261 | cs->akt_state = DEFLECT_ALERT; | ||
262 | break; | ||
263 | |||
264 | } /* switch */ | ||
265 | return (i); | ||
266 | } /* deflect_extern_action */ | ||
267 | |||
268 | /********************************/ | ||
269 | /* insert a new rule before idx */ | ||
270 | /********************************/ | ||
271 | int insertrule(int idx, divert_rule *newrule) | ||
272 | { | ||
273 | struct deflect_struc *ds, *ds1 = NULL; | ||
274 | unsigned long flags; | ||
275 | |||
276 | if (!(ds = kmalloc(sizeof(struct deflect_struc), GFP_KERNEL))) | ||
277 | return (-ENOMEM); /* no memory */ | ||
278 | |||
279 | ds->rule = *newrule; /* set rule */ | ||
280 | |||
281 | spin_lock_irqsave(&divert_lock, flags); | ||
282 | |||
283 | if (idx >= 0) { | ||
284 | ds1 = table_head; | ||
285 | while ((ds1) && (idx > 0)) | ||
286 | { idx--; | ||
287 | ds1 = ds1->next; | ||
288 | } | ||
289 | if (!ds1) idx = -1; | ||
290 | } | ||
291 | |||
292 | if (idx < 0) { | ||
293 | ds->prev = table_tail; /* previous entry */ | ||
294 | ds->next = NULL; /* end of chain */ | ||
295 | if (ds->prev) | ||
296 | ds->prev->next = ds; /* last forward */ | ||
297 | else | ||
298 | table_head = ds; /* is first entry */ | ||
299 | table_tail = ds; /* end of queue */ | ||
300 | } else { | ||
301 | ds->next = ds1; /* next entry */ | ||
302 | ds->prev = ds1->prev; /* prev entry */ | ||
303 | ds1->prev = ds; /* backward chain old element */ | ||
304 | if (!ds->prev) | ||
305 | table_head = ds; /* first element */ | ||
306 | } | ||
307 | |||
308 | spin_unlock_irqrestore(&divert_lock, flags); | ||
309 | return (0); | ||
310 | } /* insertrule */ | ||
311 | |||
312 | /***********************************/ | ||
313 | /* delete the rule at position idx */ | ||
314 | /***********************************/ | ||
315 | int deleterule(int idx) | ||
316 | { | ||
317 | struct deflect_struc *ds, *ds1; | ||
318 | unsigned long flags; | ||
319 | |||
320 | if (idx < 0) { | ||
321 | spin_lock_irqsave(&divert_lock, flags); | ||
322 | ds = table_head; | ||
323 | table_head = NULL; | ||
324 | table_tail = NULL; | ||
325 | spin_unlock_irqrestore(&divert_lock, flags); | ||
326 | while (ds) { | ||
327 | ds1 = ds; | ||
328 | ds = ds->next; | ||
329 | kfree(ds1); | ||
330 | } | ||
331 | return (0); | ||
332 | } | ||
333 | |||
334 | spin_lock_irqsave(&divert_lock, flags); | ||
335 | ds = table_head; | ||
336 | |||
337 | while ((ds) && (idx > 0)) { | ||
338 | idx--; | ||
339 | ds = ds->next; | ||
340 | } | ||
341 | |||
342 | if (!ds) { | ||
343 | spin_unlock_irqrestore(&divert_lock, flags); | ||
344 | return (-EINVAL); | ||
345 | } | ||
346 | |||
347 | if (ds->next) | ||
348 | ds->next->prev = ds->prev; /* backward chain */ | ||
349 | else | ||
350 | table_tail = ds->prev; /* end of chain */ | ||
351 | |||
352 | if (ds->prev) | ||
353 | ds->prev->next = ds->next; /* forward chain */ | ||
354 | else | ||
355 | table_head = ds->next; /* start of chain */ | ||
356 | |||
357 | spin_unlock_irqrestore(&divert_lock, flags); | ||
358 | kfree(ds); | ||
359 | return (0); | ||
360 | } /* deleterule */ | ||
361 | |||
362 | /*******************************************/ | ||
363 | /* get a pointer to a specific rule number */ | ||
364 | /*******************************************/ | ||
365 | divert_rule *getruleptr(int idx) | ||
366 | { | ||
367 | struct deflect_struc *ds = table_head; | ||
368 | |||
369 | if (idx < 0) return (NULL); | ||
370 | while ((ds) && (idx >= 0)) { | ||
371 | if (!(idx--)) { | ||
372 | return (&ds->rule); | ||
373 | break; | ||
374 | } | ||
375 | ds = ds->next; | ||
376 | } | ||
377 | return (NULL); | ||
378 | } /* getruleptr */ | ||
379 | |||
380 | /*************************************************/ | ||
381 | /* called from common module on an incoming call */ | ||
382 | /*************************************************/ | ||
383 | static int isdn_divert_icall(isdn_ctrl *ic) | ||
384 | { | ||
385 | int retval = 0; | ||
386 | unsigned long flags; | ||
387 | struct call_struc *cs = NULL; | ||
388 | struct deflect_struc *dv; | ||
389 | char *p, *p1; | ||
390 | u_char accept; | ||
391 | |||
392 | /* first check the internal deflection table */ | ||
393 | for (dv = table_head; dv; dv = dv->next) { | ||
394 | /* scan table */ | ||
395 | if (((dv->rule.callopt == 1) && (ic->command == ISDN_STAT_ICALLW)) || | ||
396 | ((dv->rule.callopt == 2) && (ic->command == ISDN_STAT_ICALL))) | ||
397 | continue; /* call option check */ | ||
398 | if (!(dv->rule.drvid & (1L << ic->driver))) | ||
399 | continue; /* driver not matching */ | ||
400 | if ((dv->rule.si1) && (dv->rule.si1 != ic->parm.setup.si1)) | ||
401 | continue; /* si1 not matching */ | ||
402 | if ((dv->rule.si2) && (dv->rule.si2 != ic->parm.setup.si2)) | ||
403 | continue; /* si2 not matching */ | ||
404 | |||
405 | p = dv->rule.my_msn; | ||
406 | p1 = ic->parm.setup.eazmsn; | ||
407 | accept = 0; | ||
408 | while (*p) { | ||
409 | /* complete compare */ | ||
410 | if (*p == '-') { | ||
411 | accept = 1; /* call accepted */ | ||
412 | break; | ||
413 | } | ||
414 | if (*p++ != *p1++) | ||
415 | break; /* not accepted */ | ||
416 | if ((!*p) && (!*p1)) | ||
417 | accept = 1; | ||
418 | } /* complete compare */ | ||
419 | if (!accept) continue; /* not accepted */ | ||
420 | |||
421 | if ((strcmp(dv->rule.caller, "0")) || | ||
422 | (ic->parm.setup.phone[0])) { | ||
423 | p = dv->rule.caller; | ||
424 | p1 = ic->parm.setup.phone; | ||
425 | accept = 0; | ||
426 | while (*p) { | ||
427 | /* complete compare */ | ||
428 | if (*p == '-') { | ||
429 | accept = 1; /* call accepted */ | ||
430 | break; | ||
431 | } | ||
432 | if (*p++ != *p1++) | ||
433 | break; /* not accepted */ | ||
434 | if ((!*p) && (!*p1)) | ||
435 | accept = 1; | ||
436 | } /* complete compare */ | ||
437 | if (!accept) continue; /* not accepted */ | ||
438 | } | ||
439 | |||
440 | switch (dv->rule.action) { | ||
441 | case DEFLECT_IGNORE: | ||
442 | return 0; | ||
443 | |||
444 | case DEFLECT_ALERT: | ||
445 | case DEFLECT_PROCEED: | ||
446 | case DEFLECT_REPORT: | ||
447 | case DEFLECT_REJECT: | ||
448 | if (dv->rule.action == DEFLECT_PROCEED) | ||
449 | if ((!if_used) || ((!extern_wait_max) && (!dv->rule.waittime))) | ||
450 | return (0); /* no external deflection needed */ | ||
451 | if (!(cs = kmalloc(sizeof(struct call_struc), GFP_ATOMIC))) | ||
452 | return (0); /* no memory */ | ||
453 | timer_setup(&cs->timer, deflect_timer_expire, 0); | ||
454 | cs->info[0] = '\0'; | ||
455 | |||
456 | cs->ics = *ic; /* copy incoming data */ | ||
457 | if (!cs->ics.parm.setup.phone[0]) strcpy(cs->ics.parm.setup.phone, "0"); | ||
458 | if (!cs->ics.parm.setup.eazmsn[0]) strcpy(cs->ics.parm.setup.eazmsn, "0"); | ||
459 | cs->ics.parm.setup.screen = dv->rule.screen; | ||
460 | if (dv->rule.waittime) | ||
461 | cs->timer.expires = jiffies + (HZ * dv->rule.waittime); | ||
462 | else if (dv->rule.action == DEFLECT_PROCEED) | ||
463 | cs->timer.expires = jiffies + (HZ * extern_wait_max); | ||
464 | else | ||
465 | cs->timer.expires = 0; | ||
466 | cs->akt_state = dv->rule.action; | ||
467 | spin_lock_irqsave(&divert_lock, flags); | ||
468 | cs->divert_id = next_id++; /* new sequence number */ | ||
469 | spin_unlock_irqrestore(&divert_lock, flags); | ||
470 | cs->prev = NULL; | ||
471 | if (cs->akt_state == DEFLECT_ALERT) { | ||
472 | strcpy(cs->deflect_dest, dv->rule.to_nr); | ||
473 | if (!cs->timer.expires) { | ||
474 | strcpy(ic->parm.setup.eazmsn, | ||
475 | "Testtext direct"); | ||
476 | ic->parm.setup.screen = dv->rule.screen; | ||
477 | strlcpy(ic->parm.setup.phone, dv->rule.to_nr, sizeof(ic->parm.setup.phone)); | ||
478 | cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ | ||
479 | cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); | ||
480 | retval = 5; | ||
481 | } else | ||
482 | retval = 1; /* alerting */ | ||
483 | } else { | ||
484 | cs->deflect_dest[0] = '\0'; | ||
485 | retval = 4; /* only proceed */ | ||
486 | } | ||
487 | snprintf(cs->info, sizeof(cs->info), | ||
488 | "%d 0x%lx %s %s %s %s 0x%x 0x%x %d %d %s\n", | ||
489 | cs->akt_state, | ||
490 | cs->divert_id, | ||
491 | divert_if.drv_to_name(cs->ics.driver), | ||
492 | (ic->command == ISDN_STAT_ICALLW) ? "1" : "0", | ||
493 | cs->ics.parm.setup.phone, | ||
494 | cs->ics.parm.setup.eazmsn, | ||
495 | cs->ics.parm.setup.si1, | ||
496 | cs->ics.parm.setup.si2, | ||
497 | cs->ics.parm.setup.screen, | ||
498 | dv->rule.waittime, | ||
499 | cs->deflect_dest); | ||
500 | if ((dv->rule.action == DEFLECT_REPORT) || | ||
501 | (dv->rule.action == DEFLECT_REJECT)) { | ||
502 | put_info_buffer(cs->info); | ||
503 | kfree(cs); /* remove */ | ||
504 | return ((dv->rule.action == DEFLECT_REPORT) ? 0 : 2); /* nothing to do */ | ||
505 | } | ||
506 | break; | ||
507 | |||
508 | default: | ||
509 | return 0; /* ignore call */ | ||
510 | } /* switch action */ | ||
511 | break; /* will break the 'for' looping */ | ||
512 | } /* scan_table */ | ||
513 | |||
514 | if (cs) { | ||
515 | cs->prev = NULL; | ||
516 | spin_lock_irqsave(&divert_lock, flags); | ||
517 | cs->next = divert_head; | ||
518 | divert_head = cs; | ||
519 | if (cs->timer.expires) add_timer(&cs->timer); | ||
520 | spin_unlock_irqrestore(&divert_lock, flags); | ||
521 | |||
522 | put_info_buffer(cs->info); | ||
523 | return (retval); | ||
524 | } else | ||
525 | return (0); | ||
526 | } /* isdn_divert_icall */ | ||
527 | |||
528 | |||
529 | void deleteprocs(void) | ||
530 | { | ||
531 | struct call_struc *cs, *cs1; | ||
532 | unsigned long flags; | ||
533 | |||
534 | spin_lock_irqsave(&divert_lock, flags); | ||
535 | cs = divert_head; | ||
536 | divert_head = NULL; | ||
537 | while (cs) { | ||
538 | del_timer(&cs->timer); | ||
539 | cs1 = cs; | ||
540 | cs = cs->next; | ||
541 | kfree(cs1); | ||
542 | } | ||
543 | spin_unlock_irqrestore(&divert_lock, flags); | ||
544 | } /* deleteprocs */ | ||
545 | |||
546 | /****************************************************/ | ||
547 | /* put a address including address type into buffer */ | ||
548 | /****************************************************/ | ||
549 | static int put_address(char *st, u_char *p, int len) | ||
550 | { | ||
551 | u_char retval = 0; | ||
552 | u_char adr_typ = 0; /* network standard */ | ||
553 | |||
554 | if (len < 2) return (retval); | ||
555 | if (*p == 0xA1) { | ||
556 | retval = *(++p) + 2; /* total length */ | ||
557 | if (retval > len) return (0); /* too short */ | ||
558 | len = retval - 2; /* remaining length */ | ||
559 | if (len < 3) return (0); | ||
560 | if ((*(++p) != 0x0A) || (*(++p) != 1)) return (0); | ||
561 | adr_typ = *(++p); | ||
562 | len -= 3; | ||
563 | p++; | ||
564 | if (len < 2) return (0); | ||
565 | if (*p++ != 0x12) return (0); | ||
566 | if (*p > len) return (0); /* check number length */ | ||
567 | len = *p++; | ||
568 | } else if (*p == 0x80) { | ||
569 | retval = *(++p) + 2; /* total length */ | ||
570 | if (retval > len) return (0); | ||
571 | len = retval - 2; | ||
572 | p++; | ||
573 | } else | ||
574 | return (0); /* invalid address information */ | ||
575 | |||
576 | sprintf(st, "%d ", adr_typ); | ||
577 | st += strlen(st); | ||
578 | if (!len) | ||
579 | *st++ = '-'; | ||
580 | else | ||
581 | while (len--) | ||
582 | *st++ = *p++; | ||
583 | *st = '\0'; | ||
584 | return (retval); | ||
585 | } /* put_address */ | ||
586 | |||
587 | /*************************************/ | ||
588 | /* report a successful interrogation */ | ||
589 | /*************************************/ | ||
590 | static int interrogate_success(isdn_ctrl *ic, struct call_struc *cs) | ||
591 | { | ||
592 | char *src = ic->parm.dss1_io.data; | ||
593 | int restlen = ic->parm.dss1_io.datalen; | ||
594 | int cnt = 1; | ||
595 | u_char n, n1; | ||
596 | char st[90], *p, *stp; | ||
597 | |||
598 | if (restlen < 2) return (-100); /* frame too short */ | ||
599 | if (*src++ != 0x30) return (-101); | ||
600 | if ((n = *src++) > 0x81) return (-102); /* invalid length field */ | ||
601 | restlen -= 2; /* remaining bytes */ | ||
602 | if (n == 0x80) { | ||
603 | if (restlen < 2) return (-103); | ||
604 | if ((*(src + restlen - 1)) || (*(src + restlen - 2))) return (-104); | ||
605 | restlen -= 2; | ||
606 | } else if (n == 0x81) { | ||
607 | n = *src++; | ||
608 | restlen--; | ||
609 | if (n > restlen) return (-105); | ||
610 | restlen = n; | ||
611 | } else if (n > restlen) | ||
612 | return (-106); | ||
613 | else | ||
614 | restlen = n; /* standard format */ | ||
615 | if (restlen < 3) return (-107); /* no procedure */ | ||
616 | if ((*src++ != 2) || (*src++ != 1) || (*src++ != 0x0B)) return (-108); | ||
617 | restlen -= 3; | ||
618 | if (restlen < 2) return (-109); /* list missing */ | ||
619 | if (*src == 0x31) { | ||
620 | src++; | ||
621 | if ((n = *src++) > 0x81) return (-110); /* invalid length field */ | ||
622 | restlen -= 2; /* remaining bytes */ | ||
623 | if (n == 0x80) { | ||
624 | if (restlen < 2) return (-111); | ||
625 | if ((*(src + restlen - 1)) || (*(src + restlen - 2))) return (-112); | ||
626 | restlen -= 2; | ||
627 | } else if (n == 0x81) { | ||
628 | n = *src++; | ||
629 | restlen--; | ||
630 | if (n > restlen) return (-113); | ||
631 | restlen = n; | ||
632 | } else if (n > restlen) | ||
633 | return (-114); | ||
634 | else | ||
635 | restlen = n; /* standard format */ | ||
636 | } /* result list header */ | ||
637 | |||
638 | while (restlen >= 2) { | ||
639 | stp = st; | ||
640 | sprintf(stp, "%d 0x%lx %d %s ", DIVERT_REPORT, ic->parm.dss1_io.ll_id, | ||
641 | cnt++, divert_if.drv_to_name(ic->driver)); | ||
642 | stp += strlen(stp); | ||
643 | if (*src++ != 0x30) return (-115); /* invalid enum */ | ||
644 | n = *src++; | ||
645 | restlen -= 2; | ||
646 | if (n > restlen) return (-116); /* enum length wrong */ | ||
647 | restlen -= n; | ||
648 | p = src; /* one entry */ | ||
649 | src += n; | ||
650 | if (!(n1 = put_address(stp, p, n & 0xFF))) continue; | ||
651 | stp += strlen(stp); | ||
652 | p += n1; | ||
653 | n -= n1; | ||
654 | if (n < 6) continue; /* no service and proc */ | ||
655 | if ((*p++ != 0x0A) || (*p++ != 1)) continue; | ||
656 | sprintf(stp, " 0x%02x ", (*p++) & 0xFF); | ||
657 | stp += strlen(stp); | ||
658 | if ((*p++ != 0x0A) || (*p++ != 1)) continue; | ||
659 | sprintf(stp, "%d ", (*p++) & 0xFF); | ||
660 | stp += strlen(stp); | ||
661 | n -= 6; | ||
662 | if (n > 2) { | ||
663 | if (*p++ != 0x30) continue; | ||
664 | if (*p > (n - 2)) continue; | ||
665 | n = *p++; | ||
666 | if (!(n1 = put_address(stp, p, n & 0xFF))) continue; | ||
667 | stp += strlen(stp); | ||
668 | } | ||
669 | sprintf(stp, "\n"); | ||
670 | put_info_buffer(st); | ||
671 | } /* while restlen */ | ||
672 | if (restlen) return (-117); | ||
673 | return (0); | ||
674 | } /* interrogate_success */ | ||
675 | |||
676 | /*********************************************/ | ||
677 | /* callback for protocol specific extensions */ | ||
678 | /*********************************************/ | ||
679 | static int prot_stat_callback(isdn_ctrl *ic) | ||
680 | { | ||
681 | struct call_struc *cs, *cs1; | ||
682 | int i; | ||
683 | unsigned long flags; | ||
684 | |||
685 | cs = divert_head; /* start of list */ | ||
686 | cs1 = NULL; | ||
687 | while (cs) { | ||
688 | if (ic->driver == cs->ics.driver) { | ||
689 | switch (cs->ics.arg) { | ||
690 | case DSS1_CMD_INVOKE: | ||
691 | if ((cs->ics.parm.dss1_io.ll_id == ic->parm.dss1_io.ll_id) && | ||
692 | (cs->ics.parm.dss1_io.hl_id == ic->parm.dss1_io.hl_id)) { | ||
693 | switch (ic->arg) { | ||
694 | case DSS1_STAT_INVOKE_ERR: | ||
695 | sprintf(cs->info, "128 0x%lx 0x%x\n", | ||
696 | ic->parm.dss1_io.ll_id, | ||
697 | ic->parm.dss1_io.timeout); | ||
698 | put_info_buffer(cs->info); | ||
699 | break; | ||
700 | |||
701 | case DSS1_STAT_INVOKE_RES: | ||
702 | switch (cs->ics.parm.dss1_io.proc) { | ||
703 | case 7: | ||
704 | case 8: | ||
705 | put_info_buffer(cs->info); | ||
706 | break; | ||
707 | |||
708 | case 11: | ||
709 | i = interrogate_success(ic, cs); | ||
710 | if (i) | ||
711 | sprintf(cs->info, "%d 0x%lx %d\n", DIVERT_REPORT, | ||
712 | ic->parm.dss1_io.ll_id, i); | ||
713 | put_info_buffer(cs->info); | ||
714 | break; | ||
715 | |||
716 | default: | ||
717 | printk(KERN_WARNING "dss1_divert: unknown proc %d\n", cs->ics.parm.dss1_io.proc); | ||
718 | break; | ||
719 | } | ||
720 | |||
721 | break; | ||
722 | |||
723 | default: | ||
724 | printk(KERN_WARNING "dss1_divert unknown invoke answer %lx\n", ic->arg); | ||
725 | break; | ||
726 | } | ||
727 | cs1 = cs; /* remember structure */ | ||
728 | cs = NULL; | ||
729 | continue; /* abort search */ | ||
730 | } /* id found */ | ||
731 | break; | ||
732 | |||
733 | case DSS1_CMD_INVOKE_ABORT: | ||
734 | printk(KERN_WARNING "dss1_divert unhandled invoke abort\n"); | ||
735 | break; | ||
736 | |||
737 | default: | ||
738 | printk(KERN_WARNING "dss1_divert unknown cmd 0x%lx\n", cs->ics.arg); | ||
739 | break; | ||
740 | } /* switch ics.arg */ | ||
741 | cs = cs->next; | ||
742 | } /* driver ok */ | ||
743 | } | ||
744 | |||
745 | if (!cs1) { | ||
746 | printk(KERN_WARNING "dss1_divert unhandled process\n"); | ||
747 | return (0); | ||
748 | } | ||
749 | |||
750 | if (cs1->ics.driver == -1) { | ||
751 | spin_lock_irqsave(&divert_lock, flags); | ||
752 | del_timer(&cs1->timer); | ||
753 | if (cs1->prev) | ||
754 | cs1->prev->next = cs1->next; /* forward link */ | ||
755 | else | ||
756 | divert_head = cs1->next; | ||
757 | if (cs1->next) | ||
758 | cs1->next->prev = cs1->prev; /* back link */ | ||
759 | spin_unlock_irqrestore(&divert_lock, flags); | ||
760 | kfree(cs1); | ||
761 | } | ||
762 | |||
763 | return (0); | ||
764 | } /* prot_stat_callback */ | ||
765 | |||
766 | |||
767 | /***************************/ | ||
768 | /* status callback from HL */ | ||
769 | /***************************/ | ||
770 | static int isdn_divert_stat_callback(isdn_ctrl *ic) | ||
771 | { | ||
772 | struct call_struc *cs, *cs1; | ||
773 | unsigned long flags; | ||
774 | int retval; | ||
775 | |||
776 | retval = -1; | ||
777 | cs = divert_head; /* start of list */ | ||
778 | while (cs) { | ||
779 | if ((ic->driver == cs->ics.driver) && | ||
780 | (ic->arg == cs->ics.arg)) { | ||
781 | switch (ic->command) { | ||
782 | case ISDN_STAT_DHUP: | ||
783 | sprintf(cs->info, "129 0x%lx\n", cs->divert_id); | ||
784 | del_timer(&cs->timer); | ||
785 | cs->ics.driver = -1; | ||
786 | break; | ||
787 | |||
788 | case ISDN_STAT_CAUSE: | ||
789 | sprintf(cs->info, "130 0x%lx %s\n", cs->divert_id, ic->parm.num); | ||
790 | break; | ||
791 | |||
792 | case ISDN_STAT_REDIR: | ||
793 | sprintf(cs->info, "131 0x%lx\n", cs->divert_id); | ||
794 | del_timer(&cs->timer); | ||
795 | cs->ics.driver = -1; | ||
796 | break; | ||
797 | |||
798 | default: | ||
799 | sprintf(cs->info, "999 0x%lx 0x%x\n", cs->divert_id, (int)(ic->command)); | ||
800 | break; | ||
801 | } | ||
802 | put_info_buffer(cs->info); | ||
803 | retval = 0; | ||
804 | } | ||
805 | cs1 = cs; | ||
806 | cs = cs->next; | ||
807 | if (cs1->ics.driver == -1) { | ||
808 | spin_lock_irqsave(&divert_lock, flags); | ||
809 | if (cs1->prev) | ||
810 | cs1->prev->next = cs1->next; /* forward link */ | ||
811 | else | ||
812 | divert_head = cs1->next; | ||
813 | if (cs1->next) | ||
814 | cs1->next->prev = cs1->prev; /* back link */ | ||
815 | spin_unlock_irqrestore(&divert_lock, flags); | ||
816 | kfree(cs1); | ||
817 | } | ||
818 | } | ||
819 | return (retval); /* not found */ | ||
820 | } /* isdn_divert_stat_callback */ | ||
821 | |||
822 | |||
823 | /********************/ | ||
824 | /* callback from ll */ | ||
825 | /********************/ | ||
826 | int ll_callback(isdn_ctrl *ic) | ||
827 | { | ||
828 | switch (ic->command) { | ||
829 | case ISDN_STAT_ICALL: | ||
830 | case ISDN_STAT_ICALLW: | ||
831 | return (isdn_divert_icall(ic)); | ||
832 | break; | ||
833 | |||
834 | case ISDN_STAT_PROT: | ||
835 | if ((ic->arg & 0xFF) == ISDN_PTYPE_EURO) { | ||
836 | if (ic->arg != DSS1_STAT_INVOKE_BRD) | ||
837 | return (prot_stat_callback(ic)); | ||
838 | else | ||
839 | return (0); /* DSS1 invoke broadcast */ | ||
840 | } else | ||
841 | return (-1); /* protocol not euro */ | ||
842 | |||
843 | default: | ||
844 | return (isdn_divert_stat_callback(ic)); | ||
845 | } | ||
846 | } /* ll_callback */ | ||
diff --git a/drivers/isdn/divert/isdn_divert.h b/drivers/isdn/divert/isdn_divert.h deleted file mode 100644 index 55033dd872c0..000000000000 --- a/drivers/isdn/divert/isdn_divert.h +++ /dev/null | |||
@@ -1,132 +0,0 @@ | |||
1 | /* $Id: isdn_divert.h,v 1.5.6.1 2001/09/23 22:24:36 kai Exp $ | ||
2 | * | ||
3 | * Header for the diversion supplementary ioctl interface. | ||
4 | * | ||
5 | * Copyright 1998 by Werner Cornelius (werner@ikt.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/ioctl.h> | ||
13 | #include <linux/types.h> | ||
14 | |||
15 | /******************************************/ | ||
16 | /* IOCTL codes for interface to user prog */ | ||
17 | /******************************************/ | ||
18 | #define DIVERT_IIOC_VERSION 0x01 /* actual version */ | ||
19 | #define IIOCGETVER _IO('I', 1) /* get version of interface */ | ||
20 | #define IIOCGETDRV _IO('I', 2) /* get driver number */ | ||
21 | #define IIOCGETNAM _IO('I', 3) /* get driver name */ | ||
22 | #define IIOCGETRULE _IO('I', 4) /* read one rule */ | ||
23 | #define IIOCMODRULE _IO('I', 5) /* modify/replace a rule */ | ||
24 | #define IIOCINSRULE _IO('I', 6) /* insert/append one rule */ | ||
25 | #define IIOCDELRULE _IO('I', 7) /* delete a rule */ | ||
26 | #define IIOCDODFACT _IO('I', 8) /* hangup/reject/alert/immediately deflect a call */ | ||
27 | #define IIOCDOCFACT _IO('I', 9) /* activate control forwarding in PBX */ | ||
28 | #define IIOCDOCFDIS _IO('I', 10) /* deactivate control forwarding in PBX */ | ||
29 | #define IIOCDOCFINT _IO('I', 11) /* interrogate control forwarding in PBX */ | ||
30 | |||
31 | /*************************************/ | ||
32 | /* states reported through interface */ | ||
33 | /*************************************/ | ||
34 | #define DEFLECT_IGNORE 0 /* ignore incoming call */ | ||
35 | #define DEFLECT_REPORT 1 /* only report */ | ||
36 | #define DEFLECT_PROCEED 2 /* deflect when externally triggered */ | ||
37 | #define DEFLECT_ALERT 3 /* alert and deflect after delay */ | ||
38 | #define DEFLECT_REJECT 4 /* reject immediately */ | ||
39 | #define DIVERT_ACTIVATE 5 /* diversion activate */ | ||
40 | #define DIVERT_DEACTIVATE 6 /* diversion deactivate */ | ||
41 | #define DIVERT_REPORT 7 /* interrogation result */ | ||
42 | #define DEFLECT_AUTODEL 255 /* only for internal use */ | ||
43 | |||
44 | #define DEFLECT_ALL_IDS 0xFFFFFFFF /* all drivers selected */ | ||
45 | |||
46 | typedef struct { | ||
47 | ulong drvid; /* driver ids, bit mapped */ | ||
48 | char my_msn[35]; /* desired msn, subaddr allowed */ | ||
49 | char caller[35]; /* caller id, partial string with * + subaddr allowed */ | ||
50 | char to_nr[35]; /* deflected to number incl. subaddress */ | ||
51 | u_char si1, si2; /* service indicators, si1=bitmask, si1+2 0 = all */ | ||
52 | u_char screen; /* screening: 0 = no info, 1 = info, 2 = nfo with nr */ | ||
53 | u_char callopt; /* option for call handling: | ||
54 | 0 = all calls | ||
55 | 1 = only non waiting calls | ||
56 | 2 = only waiting calls */ | ||
57 | u_char action; /* desired action: | ||
58 | 0 = don't report call -> ignore | ||
59 | 1 = report call, do not allow/proceed for deflection | ||
60 | 2 = report call, send proceed, wait max waittime secs | ||
61 | 3 = report call, alert and deflect after waittime | ||
62 | 4 = report call, reject immediately | ||
63 | actions 1-2 only take place if interface is opened | ||
64 | */ | ||
65 | u_char waittime; /* maximum wait time for proceeding */ | ||
66 | } divert_rule; | ||
67 | |||
68 | typedef union { | ||
69 | int drv_version; /* return of driver version */ | ||
70 | struct { | ||
71 | int drvid; /* id of driver */ | ||
72 | char drvnam[30]; /* name of driver */ | ||
73 | } getid; | ||
74 | struct { | ||
75 | int ruleidx; /* index of rule */ | ||
76 | divert_rule rule; /* rule parms */ | ||
77 | } getsetrule; | ||
78 | struct { | ||
79 | u_char subcmd; /* 0 = hangup/reject, | ||
80 | 1 = alert, | ||
81 | 2 = deflect */ | ||
82 | ulong callid; /* id of call delivered by ascii output */ | ||
83 | char to_nr[35]; /* destination when deflect, | ||
84 | else uus1 string (maxlen 31), | ||
85 | data from rule used if empty */ | ||
86 | } fwd_ctrl; | ||
87 | struct { | ||
88 | int drvid; /* id of driver */ | ||
89 | u_char cfproc; /* cfu = 0, cfb = 1, cfnr = 2 */ | ||
90 | ulong procid; /* process id returned when no error */ | ||
91 | u_char service; /* basically coded service, 0 = all */ | ||
92 | char msn[25]; /* desired msn, empty = all */ | ||
93 | char fwd_nr[35];/* forwarded to number + subaddress */ | ||
94 | } cf_ctrl; | ||
95 | } divert_ioctl; | ||
96 | |||
97 | #ifdef __KERNEL__ | ||
98 | |||
99 | #include <linux/isdnif.h> | ||
100 | #include <linux/isdn_divertif.h> | ||
101 | |||
102 | #define AUTODEL_TIME 30 /* timeout in s to delete internal entries */ | ||
103 | |||
104 | /**************************************************/ | ||
105 | /* structure keeping ascii info for device output */ | ||
106 | /**************************************************/ | ||
107 | struct divert_info { | ||
108 | struct divert_info *next; | ||
109 | ulong usage_cnt; /* number of files still to work */ | ||
110 | char info_start[2]; /* info string start */ | ||
111 | }; | ||
112 | |||
113 | |||
114 | /**************/ | ||
115 | /* Prototypes */ | ||
116 | /**************/ | ||
117 | extern spinlock_t divert_lock; | ||
118 | |||
119 | extern ulong if_used; /* number of interface users */ | ||
120 | extern int divert_dev_deinit(void); | ||
121 | extern int divert_dev_init(void); | ||
122 | extern void put_info_buffer(char *); | ||
123 | extern int ll_callback(isdn_ctrl *); | ||
124 | extern isdn_divert_if divert_if; | ||
125 | extern divert_rule *getruleptr(int); | ||
126 | extern int insertrule(int, divert_rule *); | ||
127 | extern int deleterule(int); | ||
128 | extern void deleteprocs(void); | ||
129 | extern int deflect_extern_action(u_char, ulong, char *); | ||
130 | extern int cf_command(int, int, u_char, char *, u_char, char *, ulong *); | ||
131 | |||
132 | #endif /* __KERNEL__ */ | ||
diff --git a/drivers/isdn/i4l/Kconfig b/drivers/isdn/i4l/Kconfig deleted file mode 100644 index cacde8de38a3..000000000000 --- a/drivers/isdn/i4l/Kconfig +++ /dev/null | |||
@@ -1,127 +0,0 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0-only | ||
2 | # | ||
3 | # Old ISDN4Linux config | ||
4 | # | ||
5 | |||
6 | if ISDN_I4L | ||
7 | |||
8 | config ISDN_PPP | ||
9 | bool "Support synchronous PPP" | ||
10 | depends on INET | ||
11 | select SLHC | ||
12 | help | ||
13 | Over digital connections such as ISDN, there is no need to | ||
14 | synchronize sender and recipient's clocks with start and stop bits | ||
15 | as is done over analog telephone lines. Instead, one can use | ||
16 | "synchronous PPP". Saying Y here will include this protocol. This | ||
17 | protocol is used by Cisco and Sun for example. So you want to say Y | ||
18 | here if the other end of your ISDN connection supports it. You will | ||
19 | need a special version of pppd (called ipppd) for using this | ||
20 | feature. See <file:Documentation/isdn/README.syncppp> and | ||
21 | <file:Documentation/isdn/syncPPP.FAQ> for more information. | ||
22 | |||
23 | config ISDN_PPP_VJ | ||
24 | bool "Use VJ-compression with synchronous PPP" | ||
25 | depends on ISDN_PPP | ||
26 | help | ||
27 | This enables Van Jacobson header compression for synchronous PPP. | ||
28 | Say Y if the other end of the connection supports it. | ||
29 | |||
30 | config ISDN_MPP | ||
31 | bool "Support generic MP (RFC 1717)" | ||
32 | depends on ISDN_PPP | ||
33 | help | ||
34 | With synchronous PPP enabled, it is possible to increase throughput | ||
35 | by bundling several ISDN-connections, using this protocol. See | ||
36 | <file:Documentation/isdn/README.syncppp> for more information. | ||
37 | |||
38 | config IPPP_FILTER | ||
39 | bool "Filtering for synchronous PPP" | ||
40 | depends on ISDN_PPP | ||
41 | help | ||
42 | Say Y here if you want to be able to filter the packets passing over | ||
43 | IPPP interfaces. This allows you to control which packets count as | ||
44 | activity (i.e. which packets will reset the idle timer or bring up | ||
45 | a demand-dialled link) and which packets are to be dropped entirely. | ||
46 | You need to say Y here if you wish to use the pass-filter and | ||
47 | active-filter options to ipppd. | ||
48 | |||
49 | config ISDN_PPP_BSDCOMP | ||
50 | tristate "Support BSD compression" | ||
51 | depends on ISDN_PPP | ||
52 | help | ||
53 | Support for the BSD-Compress compression method for PPP, which uses | ||
54 | the LZW compression method to compress each PPP packet before it is | ||
55 | sent over the wire. The machine at the other end of the PPP link | ||
56 | (usually your ISP) has to support the BSD-Compress compression | ||
57 | method as well for this to be useful. Even if they don't support it, | ||
58 | it is safe to say Y here. | ||
59 | |||
60 | config ISDN_AUDIO | ||
61 | bool "Support audio via ISDN" | ||
62 | help | ||
63 | If you say Y here, the modem-emulator will support a subset of the | ||
64 | EIA Class 8 Voice commands. Using a getty with voice-support | ||
65 | (mgetty+sendfax by <gert@greenie.muc.de> with an extension, available | ||
66 | with the ISDN utility package for example), you will be able to use | ||
67 | your Linux box as an ISDN-answering machine. Of course, this must be | ||
68 | supported by the lowlevel driver also. Currently, the HiSax driver | ||
69 | is the only voice-supporting driver. See | ||
70 | <file:Documentation/isdn/README.audio> for more information. | ||
71 | |||
72 | config ISDN_TTY_FAX | ||
73 | bool "Support AT-Fax Class 1 and 2 commands" | ||
74 | depends on ISDN_AUDIO | ||
75 | help | ||
76 | If you say Y here, the modem-emulator will support a subset of the | ||
77 | Fax Class 1 and 2 commands. Using a getty with fax-support | ||
78 | (mgetty+sendfax, hylafax), you will be able to use your Linux box as | ||
79 | an ISDN-fax-machine. This must be supported by the lowlevel driver | ||
80 | also. See <file:Documentation/isdn/README.fax> for more information. | ||
81 | |||
82 | config ISDN_X25 | ||
83 | bool "X.25 PLP on top of ISDN" | ||
84 | depends on X25 | ||
85 | help | ||
86 | This feature provides the X.25 protocol over ISDN connections. | ||
87 | See <file:Documentation/isdn/README.x25> for more information | ||
88 | if you are thinking about using this. | ||
89 | |||
90 | |||
91 | menu "ISDN feature submodules" | ||
92 | |||
93 | config ISDN_DRV_LOOP | ||
94 | tristate "isdnloop support" | ||
95 | depends on BROKEN_ON_SMP | ||
96 | help | ||
97 | This driver provides a virtual ISDN card. Its primary purpose is | ||
98 | testing of linklevel features or configuration without getting | ||
99 | charged by your service-provider for lots of phone calls. | ||
100 | You need will need the loopctrl utility from the latest isdn4k-utils | ||
101 | package to set up this driver. | ||
102 | |||
103 | config ISDN_DIVERSION | ||
104 | tristate "Support isdn diversion services" | ||
105 | help | ||
106 | This option allows you to use some supplementary diversion | ||
107 | services in conjunction with the HiSax driver on an EURO/DSS1 | ||
108 | line. | ||
109 | |||
110 | Supported options are CD (call deflection), CFU (Call forward | ||
111 | unconditional), CFB (Call forward when busy) and CFNR (call forward | ||
112 | not reachable). Additionally the actual CFU, CFB and CFNR state may | ||
113 | be interrogated. | ||
114 | |||
115 | The use of CFU, CFB, CFNR and interrogation may be limited to some | ||
116 | countries. The keypad protocol is still not implemented. CD should | ||
117 | work in all countries if the service has been subscribed to. | ||
118 | |||
119 | Please read the file <file:Documentation/isdn/README.diversion>. | ||
120 | |||
121 | endmenu | ||
122 | |||
123 | comment "ISDN4Linux hardware drivers" | ||
124 | |||
125 | # end ISDN_I4L | ||
126 | endif | ||
127 | |||
diff --git a/drivers/isdn/i4l/Makefile b/drivers/isdn/i4l/Makefile index be77500c9e86..11fe697739d5 100644 --- a/drivers/isdn/i4l/Makefile +++ b/drivers/isdn/i4l/Makefile | |||
@@ -3,18 +3,4 @@ | |||
3 | 3 | ||
4 | # Each configuration option enables a list of files. | 4 | # Each configuration option enables a list of files. |
5 | 5 | ||
6 | obj-$(CONFIG_ISDN_I4L) += isdn.o | ||
7 | obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o | ||
8 | obj-$(CONFIG_ISDN_HDLC) += isdnhdlc.o | 6 | obj-$(CONFIG_ISDN_HDLC) += isdnhdlc.o |
9 | |||
10 | # Multipart objects. | ||
11 | |||
12 | isdn-y := isdn_net.o isdn_tty.o isdn_v110.o isdn_common.o | ||
13 | |||
14 | # Optional parts of multipart objects. | ||
15 | |||
16 | isdn-$(CONFIG_ISDN_PPP) += isdn_ppp.o | ||
17 | isdn-$(CONFIG_ISDN_X25) += isdn_concap.o isdn_x25iface.o | ||
18 | isdn-$(CONFIG_ISDN_AUDIO) += isdn_audio.o | ||
19 | isdn-$(CONFIG_ISDN_TTY_FAX) += isdn_ttyfax.o | ||
20 | |||
diff --git a/drivers/isdn/i4l/isdn_audio.c b/drivers/isdn/i4l/isdn_audio.c deleted file mode 100644 index b6bcd1eca128..000000000000 --- a/drivers/isdn/i4l/isdn_audio.c +++ /dev/null | |||
@@ -1,711 +0,0 @@ | |||
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 <linux/slab.h> | ||
16 | #include "isdn_audio.h" | ||
17 | #include "isdn_common.h" | ||
18 | |||
19 | char *isdn_audio_revision = "$Revision: 1.1.2.2 $"; | ||
20 | |||
21 | /* | ||
22 | * Misc. lookup-tables. | ||
23 | */ | ||
24 | |||
25 | /* ulaw -> signed 16-bit */ | ||
26 | static short isdn_audio_ulaw_to_s16[] = | ||
27 | { | ||
28 | 0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84, | ||
29 | 0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84, | ||
30 | 0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84, | ||
31 | 0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84, | ||
32 | 0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804, | ||
33 | 0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004, | ||
34 | 0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444, | ||
35 | 0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844, | ||
36 | 0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64, | ||
37 | 0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64, | ||
38 | 0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74, | ||
39 | 0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74, | ||
40 | 0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc, | ||
41 | 0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c, | ||
42 | 0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0, | ||
43 | 0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0x0000, | ||
44 | 0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c, | ||
45 | 0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c, | ||
46 | 0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c, | ||
47 | 0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c, | ||
48 | 0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc, | ||
49 | 0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc, | ||
50 | 0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc, | ||
51 | 0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc, | ||
52 | 0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c, | ||
53 | 0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c, | ||
54 | 0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c, | ||
55 | 0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c, | ||
56 | 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, | ||
57 | 0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084, | ||
58 | 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, | ||
59 | 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 | ||
60 | }; | ||
61 | |||
62 | /* alaw -> signed 16-bit */ | ||
63 | static short isdn_audio_alaw_to_s16[] = | ||
64 | { | ||
65 | 0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4, | ||
66 | 0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74, | ||
67 | 0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4, | ||
68 | 0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64, | ||
69 | 0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4, | ||
70 | 0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4, | ||
71 | 0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4, | ||
72 | 0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4, | ||
73 | 0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64, | ||
74 | 0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34, | ||
75 | 0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844, | ||
76 | 0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24, | ||
77 | 0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64, | ||
78 | 0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4, | ||
79 | 0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964, | ||
80 | 0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4, | ||
81 | 0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24, | ||
82 | 0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94, | ||
83 | 0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924, | ||
84 | 0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94, | ||
85 | 0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24, | ||
86 | 0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14, | ||
87 | 0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24, | ||
88 | 0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14, | ||
89 | 0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4, | ||
90 | 0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54, | ||
91 | 0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4, | ||
92 | 0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64, | ||
93 | 0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4, | ||
94 | 0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4, | ||
95 | 0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4, | ||
96 | 0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4 | ||
97 | }; | ||
98 | |||
99 | /* alaw -> ulaw */ | ||
100 | static char isdn_audio_alaw_to_ulaw[] = | ||
101 | { | ||
102 | 0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49, | ||
103 | 0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57, | ||
104 | 0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41, | ||
105 | 0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f, | ||
106 | 0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d, | ||
107 | 0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b, | ||
108 | 0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45, | ||
109 | 0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53, | ||
110 | 0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47, | ||
111 | 0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55, | ||
112 | 0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f, | ||
113 | 0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e, | ||
114 | 0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b, | ||
115 | 0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59, | ||
116 | 0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43, | ||
117 | 0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51, | ||
118 | 0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a, | ||
119 | 0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58, | ||
120 | 0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42, | ||
121 | 0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50, | ||
122 | 0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e, | ||
123 | 0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c, | ||
124 | 0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46, | ||
125 | 0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54, | ||
126 | 0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48, | ||
127 | 0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56, | ||
128 | 0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40, | ||
129 | 0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f, | ||
130 | 0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c, | ||
131 | 0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a, | ||
132 | 0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44, | ||
133 | 0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52 | ||
134 | }; | ||
135 | |||
136 | /* ulaw -> alaw */ | ||
137 | static char isdn_audio_ulaw_to_alaw[] = | ||
138 | { | ||
139 | 0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35, | ||
140 | 0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25, | ||
141 | 0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d, | ||
142 | 0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d, | ||
143 | 0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31, | ||
144 | 0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21, | ||
145 | 0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9, | ||
146 | 0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9, | ||
147 | 0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47, | ||
148 | 0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf, | ||
149 | 0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f, | ||
150 | 0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33, | ||
151 | 0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23, | ||
152 | 0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b, | ||
153 | 0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b, | ||
154 | 0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b, | ||
155 | 0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34, | ||
156 | 0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24, | ||
157 | 0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c, | ||
158 | 0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c, | ||
159 | 0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30, | ||
160 | 0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20, | ||
161 | 0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8, | ||
162 | 0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8, | ||
163 | 0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46, | ||
164 | 0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde, | ||
165 | 0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e, | ||
166 | 0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32, | ||
167 | 0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22, | ||
168 | 0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a, | ||
169 | 0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a, | ||
170 | 0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a | ||
171 | }; | ||
172 | |||
173 | #define NCOEFF 8 /* number of frequencies to be analyzed */ | ||
174 | #define DTMF_TRESH 4000 /* above this is dtmf */ | ||
175 | #define SILENCE_TRESH 200 /* below this is silence */ | ||
176 | #define AMP_BITS 9 /* bits per sample, reduced to avoid overflow */ | ||
177 | #define LOGRP 0 | ||
178 | #define HIGRP 1 | ||
179 | |||
180 | /* For DTMF recognition: | ||
181 | * 2 * cos(2 * PI * k / N) precalculated for all k | ||
182 | */ | ||
183 | static int cos2pik[NCOEFF] = | ||
184 | { | ||
185 | 55813, 53604, 51193, 48591, 38114, 33057, 25889, 18332 | ||
186 | }; | ||
187 | |||
188 | static char dtmf_matrix[4][4] = | ||
189 | { | ||
190 | {'1', '2', '3', 'A'}, | ||
191 | {'4', '5', '6', 'B'}, | ||
192 | {'7', '8', '9', 'C'}, | ||
193 | {'*', '0', '#', 'D'} | ||
194 | }; | ||
195 | |||
196 | static inline void | ||
197 | isdn_audio_tlookup(const u_char *table, u_char *buff, unsigned long n) | ||
198 | { | ||
199 | #ifdef __i386__ | ||
200 | unsigned long d0, d1, d2, d3; | ||
201 | __asm__ __volatile__( | ||
202 | "cld\n" | ||
203 | "1:\tlodsb\n\t" | ||
204 | "xlatb\n\t" | ||
205 | "stosb\n\t" | ||
206 | "loop 1b\n\t" | ||
207 | : "=&b"(d0), "=&c"(d1), "=&D"(d2), "=&S"(d3) | ||
208 | : "0"((long) table), "1"(n), "2"((long) buff), "3"((long) buff) | ||
209 | : "memory", "ax"); | ||
210 | #else | ||
211 | while (n--) | ||
212 | *buff = table[*(unsigned char *)buff], buff++; | ||
213 | #endif | ||
214 | } | ||
215 | |||
216 | void | ||
217 | isdn_audio_ulaw2alaw(unsigned char *buff, unsigned long len) | ||
218 | { | ||
219 | isdn_audio_tlookup(isdn_audio_ulaw_to_alaw, buff, len); | ||
220 | } | ||
221 | |||
222 | void | ||
223 | isdn_audio_alaw2ulaw(unsigned char *buff, unsigned long len) | ||
224 | { | ||
225 | isdn_audio_tlookup(isdn_audio_alaw_to_ulaw, buff, len); | ||
226 | } | ||
227 | |||
228 | /* | ||
229 | * linear <-> adpcm conversion stuff | ||
230 | * Most parts from the mgetty-package. | ||
231 | * (C) by Gert Doering and Klaus Weidner | ||
232 | * Used by permission of Gert Doering | ||
233 | */ | ||
234 | |||
235 | |||
236 | #define ZEROTRAP /* turn on the trap as per the MIL-STD */ | ||
237 | #undef ZEROTRAP | ||
238 | #define BIAS 0x84 /* define the add-in bias for 16 bit samples */ | ||
239 | #define CLIP 32635 | ||
240 | |||
241 | static unsigned char | ||
242 | isdn_audio_linear2ulaw(int sample) | ||
243 | { | ||
244 | static int exp_lut[256] = | ||
245 | { | ||
246 | 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, | ||
247 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, | ||
248 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, | ||
249 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, | ||
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 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, | ||
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 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | ||
262 | }; | ||
263 | int sign, | ||
264 | exponent, | ||
265 | mantissa; | ||
266 | unsigned char ulawbyte; | ||
267 | |||
268 | /* Get the sample into sign-magnitude. */ | ||
269 | sign = (sample >> 8) & 0x80; /* set aside the sign */ | ||
270 | if (sign != 0) | ||
271 | sample = -sample; /* get magnitude */ | ||
272 | if (sample > CLIP) | ||
273 | sample = CLIP; /* clip the magnitude */ | ||
274 | |||
275 | /* Convert from 16 bit linear to ulaw. */ | ||
276 | sample = sample + BIAS; | ||
277 | exponent = exp_lut[(sample >> 7) & 0xFF]; | ||
278 | mantissa = (sample >> (exponent + 3)) & 0x0F; | ||
279 | ulawbyte = ~(sign | (exponent << 4) | mantissa); | ||
280 | #ifdef ZEROTRAP | ||
281 | /* optional CCITT trap */ | ||
282 | if (ulawbyte == 0) | ||
283 | ulawbyte = 0x02; | ||
284 | #endif | ||
285 | return (ulawbyte); | ||
286 | } | ||
287 | |||
288 | |||
289 | static int Mx[3][8] = | ||
290 | { | ||
291 | {0x3800, 0x5600, 0, 0, 0, 0, 0, 0}, | ||
292 | {0x399a, 0x3a9f, 0x4d14, 0x6607, 0, 0, 0, 0}, | ||
293 | {0x3556, 0x3556, 0x399A, 0x3A9F, 0x4200, 0x4D14, 0x6607, 0x6607}, | ||
294 | }; | ||
295 | |||
296 | static int bitmask[9] = | ||
297 | { | ||
298 | 0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff | ||
299 | }; | ||
300 | |||
301 | static int | ||
302 | isdn_audio_get_bits(adpcm_state *s, unsigned char **in, int *len) | ||
303 | { | ||
304 | while (s->nleft < s->nbits) { | ||
305 | int d = *((*in)++); | ||
306 | (*len)--; | ||
307 | s->word = (s->word << 8) | d; | ||
308 | s->nleft += 8; | ||
309 | } | ||
310 | s->nleft -= s->nbits; | ||
311 | return (s->word >> s->nleft) & bitmask[s->nbits]; | ||
312 | } | ||
313 | |||
314 | static void | ||
315 | isdn_audio_put_bits(int data, int nbits, adpcm_state *s, | ||
316 | unsigned char **out, int *len) | ||
317 | { | ||
318 | s->word = (s->word << nbits) | (data & bitmask[nbits]); | ||
319 | s->nleft += nbits; | ||
320 | while (s->nleft >= 8) { | ||
321 | int d = (s->word >> (s->nleft - 8)); | ||
322 | *(out[0]++) = d & 255; | ||
323 | (*len)++; | ||
324 | s->nleft -= 8; | ||
325 | } | ||
326 | } | ||
327 | |||
328 | adpcm_state * | ||
329 | isdn_audio_adpcm_init(adpcm_state *s, int nbits) | ||
330 | { | ||
331 | if (!s) | ||
332 | s = kmalloc(sizeof(adpcm_state), GFP_ATOMIC); | ||
333 | if (s) { | ||
334 | s->a = 0; | ||
335 | s->d = 5; | ||
336 | s->word = 0; | ||
337 | s->nleft = 0; | ||
338 | s->nbits = nbits; | ||
339 | } | ||
340 | return s; | ||
341 | } | ||
342 | |||
343 | dtmf_state * | ||
344 | isdn_audio_dtmf_init(dtmf_state *s) | ||
345 | { | ||
346 | if (!s) | ||
347 | s = kmalloc(sizeof(dtmf_state), GFP_ATOMIC); | ||
348 | if (s) { | ||
349 | s->idx = 0; | ||
350 | s->last = ' '; | ||
351 | } | ||
352 | return s; | ||
353 | } | ||
354 | |||
355 | /* | ||
356 | * Decompression of adpcm data to a/u-law | ||
357 | * | ||
358 | */ | ||
359 | |||
360 | int | ||
361 | isdn_audio_adpcm2xlaw(adpcm_state *s, int fmt, unsigned char *in, | ||
362 | unsigned char *out, int len) | ||
363 | { | ||
364 | int a = s->a; | ||
365 | int d = s->d; | ||
366 | int nbits = s->nbits; | ||
367 | int olen = 0; | ||
368 | |||
369 | while (len) { | ||
370 | int e = isdn_audio_get_bits(s, &in, &len); | ||
371 | int sign; | ||
372 | |||
373 | if (nbits == 4 && e == 0) | ||
374 | d = 4; | ||
375 | sign = (e >> (nbits - 1)) ? -1 : 1; | ||
376 | e &= bitmask[nbits - 1]; | ||
377 | a += sign * ((e << 1) + 1) * d >> 1; | ||
378 | if (d & 1) | ||
379 | a++; | ||
380 | if (fmt) | ||
381 | *out++ = isdn_audio_ulaw_to_alaw[ | ||
382 | isdn_audio_linear2ulaw(a << 2)]; | ||
383 | else | ||
384 | *out++ = isdn_audio_linear2ulaw(a << 2); | ||
385 | olen++; | ||
386 | d = (d * Mx[nbits - 2][e] + 0x2000) >> 14; | ||
387 | if (d < 5) | ||
388 | d = 5; | ||
389 | } | ||
390 | s->a = a; | ||
391 | s->d = d; | ||
392 | return olen; | ||
393 | } | ||
394 | |||
395 | int | ||
396 | isdn_audio_xlaw2adpcm(adpcm_state *s, int fmt, unsigned char *in, | ||
397 | unsigned char *out, int len) | ||
398 | { | ||
399 | int a = s->a; | ||
400 | int d = s->d; | ||
401 | int nbits = s->nbits; | ||
402 | int olen = 0; | ||
403 | |||
404 | while (len--) { | ||
405 | int e = 0, | ||
406 | nmax = 1 << (nbits - 1); | ||
407 | int sign, | ||
408 | delta; | ||
409 | |||
410 | if (fmt) | ||
411 | delta = (isdn_audio_alaw_to_s16[*in++] >> 2) - a; | ||
412 | else | ||
413 | delta = (isdn_audio_ulaw_to_s16[*in++] >> 2) - a; | ||
414 | if (delta < 0) { | ||
415 | e = nmax; | ||
416 | delta = -delta; | ||
417 | } | ||
418 | while (--nmax && delta > d) { | ||
419 | delta -= d; | ||
420 | e++; | ||
421 | } | ||
422 | if (nbits == 4 && ((e & 0x0f) == 0)) | ||
423 | e = 8; | ||
424 | isdn_audio_put_bits(e, nbits, s, &out, &olen); | ||
425 | sign = (e >> (nbits - 1)) ? -1 : 1; | ||
426 | e &= bitmask[nbits - 1]; | ||
427 | |||
428 | a += sign * ((e << 1) + 1) * d >> 1; | ||
429 | if (d & 1) | ||
430 | a++; | ||
431 | d = (d * Mx[nbits - 2][e] + 0x2000) >> 14; | ||
432 | if (d < 5) | ||
433 | d = 5; | ||
434 | } | ||
435 | s->a = a; | ||
436 | s->d = d; | ||
437 | return olen; | ||
438 | } | ||
439 | |||
440 | /* | ||
441 | * Goertzel algorithm. | ||
442 | * See http://ptolemy.eecs.berkeley.edu/papers/96/dtmf_ict/ | ||
443 | * for more info. | ||
444 | * Result is stored into an sk_buff and queued up for later | ||
445 | * evaluation. | ||
446 | */ | ||
447 | static void | ||
448 | isdn_audio_goertzel(int *sample, modem_info *info) | ||
449 | { | ||
450 | int sk, | ||
451 | sk1, | ||
452 | sk2; | ||
453 | int k, | ||
454 | n; | ||
455 | struct sk_buff *skb; | ||
456 | int *result; | ||
457 | |||
458 | skb = dev_alloc_skb(sizeof(int) * NCOEFF); | ||
459 | if (!skb) { | ||
460 | printk(KERN_WARNING | ||
461 | "isdn_audio: Could not alloc DTMF result for ttyI%d\n", | ||
462 | info->line); | ||
463 | return; | ||
464 | } | ||
465 | result = skb_put(skb, sizeof(int) * NCOEFF); | ||
466 | for (k = 0; k < NCOEFF; k++) { | ||
467 | sk = sk1 = sk2 = 0; | ||
468 | for (n = 0; n < DTMF_NPOINTS; n++) { | ||
469 | sk = sample[n] + ((cos2pik[k] * sk1) >> 15) - sk2; | ||
470 | sk2 = sk1; | ||
471 | sk1 = sk; | ||
472 | } | ||
473 | /* Avoid overflows */ | ||
474 | sk >>= 1; | ||
475 | sk2 >>= 1; | ||
476 | /* compute |X(k)|**2 */ | ||
477 | /* report overflows. This should not happen. */ | ||
478 | /* Comment this out if desired */ | ||
479 | if (sk < -32768 || sk > 32767) | ||
480 | printk(KERN_DEBUG | ||
481 | "isdn_audio: dtmf goertzel overflow, sk=%d\n", sk); | ||
482 | if (sk2 < -32768 || sk2 > 32767) | ||
483 | printk(KERN_DEBUG | ||
484 | "isdn_audio: dtmf goertzel overflow, sk2=%d\n", sk2); | ||
485 | result[k] = | ||
486 | ((sk * sk) >> AMP_BITS) - | ||
487 | ((((cos2pik[k] * sk) >> 15) * sk2) >> AMP_BITS) + | ||
488 | ((sk2 * sk2) >> AMP_BITS); | ||
489 | } | ||
490 | skb_queue_tail(&info->dtmf_queue, skb); | ||
491 | isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); | ||
492 | } | ||
493 | |||
494 | void | ||
495 | isdn_audio_eval_dtmf(modem_info *info) | ||
496 | { | ||
497 | struct sk_buff *skb; | ||
498 | int *result; | ||
499 | dtmf_state *s; | ||
500 | int silence; | ||
501 | int i; | ||
502 | int di; | ||
503 | int ch; | ||
504 | int grp[2]; | ||
505 | char what; | ||
506 | char *p; | ||
507 | int thresh; | ||
508 | |||
509 | while ((skb = skb_dequeue(&info->dtmf_queue))) { | ||
510 | result = (int *) skb->data; | ||
511 | s = info->dtmf_state; | ||
512 | grp[LOGRP] = grp[HIGRP] = -1; | ||
513 | silence = 0; | ||
514 | thresh = 0; | ||
515 | for (i = 0; i < NCOEFF; i++) { | ||
516 | if (result[i] > DTMF_TRESH) { | ||
517 | if (result[i] > thresh) | ||
518 | thresh = result[i]; | ||
519 | } | ||
520 | else if (result[i] < SILENCE_TRESH) | ||
521 | silence++; | ||
522 | } | ||
523 | if (silence == NCOEFF) | ||
524 | what = ' '; | ||
525 | else { | ||
526 | if (thresh > 0) { | ||
527 | thresh = thresh >> 4; /* touchtones must match within 12 dB */ | ||
528 | for (i = 0; i < NCOEFF; i++) { | ||
529 | if (result[i] < thresh) | ||
530 | continue; /* ignore */ | ||
531 | /* good level found. This is allowed only one time per group */ | ||
532 | if (i < NCOEFF / 2) { | ||
533 | /* lowgroup*/ | ||
534 | if (grp[LOGRP] >= 0) { | ||
535 | // Bad. Another tone found. */ | ||
536 | grp[LOGRP] = -1; | ||
537 | break; | ||
538 | } | ||
539 | else | ||
540 | grp[LOGRP] = i; | ||
541 | } | ||
542 | else { /* higroup */ | ||
543 | if (grp[HIGRP] >= 0) { // Bad. Another tone found. */ | ||
544 | grp[HIGRP] = -1; | ||
545 | break; | ||
546 | } | ||
547 | else | ||
548 | grp[HIGRP] = i - NCOEFF/2; | ||
549 | } | ||
550 | } | ||
551 | if ((grp[LOGRP] >= 0) && (grp[HIGRP] >= 0)) { | ||
552 | what = dtmf_matrix[grp[LOGRP]][grp[HIGRP]]; | ||
553 | if (s->last != ' ' && s->last != '.') | ||
554 | s->last = what; /* min. 1 non-DTMF between DTMF */ | ||
555 | } else | ||
556 | what = '.'; | ||
557 | } | ||
558 | else | ||
559 | what = '.'; | ||
560 | } | ||
561 | if ((what != s->last) && (what != ' ') && (what != '.')) { | ||
562 | printk(KERN_DEBUG "dtmf: tt='%c'\n", what); | ||
563 | p = skb->data; | ||
564 | *p++ = 0x10; | ||
565 | *p = what; | ||
566 | skb_trim(skb, 2); | ||
567 | ISDN_AUDIO_SKB_DLECOUNT(skb) = 0; | ||
568 | ISDN_AUDIO_SKB_LOCK(skb) = 0; | ||
569 | di = info->isdn_driver; | ||
570 | ch = info->isdn_channel; | ||
571 | __skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb); | ||
572 | dev->drv[di]->rcvcount[ch] += 2; | ||
573 | /* Schedule dequeuing */ | ||
574 | if ((dev->modempoll) && (info->rcvsched)) | ||
575 | isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); | ||
576 | wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]); | ||
577 | } else | ||
578 | kfree_skb(skb); | ||
579 | s->last = what; | ||
580 | } | ||
581 | } | ||
582 | |||
583 | /* | ||
584 | * Decode DTMF tones, queue result in separate sk_buf for | ||
585 | * later examination. | ||
586 | * Parameters: | ||
587 | * s = pointer to state-struct. | ||
588 | * buf = input audio data | ||
589 | * len = size of audio data. | ||
590 | * fmt = audio data format (0 = ulaw, 1 = alaw) | ||
591 | */ | ||
592 | void | ||
593 | isdn_audio_calc_dtmf(modem_info *info, unsigned char *buf, int len, int fmt) | ||
594 | { | ||
595 | dtmf_state *s = info->dtmf_state; | ||
596 | int i; | ||
597 | int c; | ||
598 | |||
599 | while (len) { | ||
600 | c = DTMF_NPOINTS - s->idx; | ||
601 | if (c > len) | ||
602 | c = len; | ||
603 | if (c <= 0) | ||
604 | break; | ||
605 | for (i = 0; i < c; i++) { | ||
606 | if (fmt) | ||
607 | s->buf[s->idx++] = | ||
608 | isdn_audio_alaw_to_s16[*buf++] >> (15 - AMP_BITS); | ||
609 | else | ||
610 | s->buf[s->idx++] = | ||
611 | isdn_audio_ulaw_to_s16[*buf++] >> (15 - AMP_BITS); | ||
612 | } | ||
613 | if (s->idx == DTMF_NPOINTS) { | ||
614 | isdn_audio_goertzel(s->buf, info); | ||
615 | s->idx = 0; | ||
616 | } | ||
617 | len -= c; | ||
618 | } | ||
619 | } | ||
620 | |||
621 | silence_state * | ||
622 | isdn_audio_silence_init(silence_state *s) | ||
623 | { | ||
624 | if (!s) | ||
625 | s = kmalloc(sizeof(silence_state), GFP_ATOMIC); | ||
626 | if (s) { | ||
627 | s->idx = 0; | ||
628 | s->state = 0; | ||
629 | } | ||
630 | return s; | ||
631 | } | ||
632 | |||
633 | void | ||
634 | isdn_audio_calc_silence(modem_info *info, unsigned char *buf, int len, int fmt) | ||
635 | { | ||
636 | silence_state *s = info->silence_state; | ||
637 | int i; | ||
638 | signed char c; | ||
639 | |||
640 | if (!info->emu.vpar[1]) return; | ||
641 | |||
642 | for (i = 0; i < len; i++) { | ||
643 | if (fmt) | ||
644 | c = isdn_audio_alaw_to_ulaw[*buf++]; | ||
645 | else | ||
646 | c = *buf++; | ||
647 | |||
648 | if (c > 0) c -= 128; | ||
649 | c = abs(c); | ||
650 | |||
651 | if (c > (info->emu.vpar[1] * 4)) { | ||
652 | s->idx = 0; | ||
653 | s->state = 1; | ||
654 | } else { | ||
655 | if (s->idx < 210000) s->idx++; | ||
656 | } | ||
657 | } | ||
658 | } | ||
659 | |||
660 | void | ||
661 | isdn_audio_put_dle_code(modem_info *info, u_char code) | ||
662 | { | ||
663 | struct sk_buff *skb; | ||
664 | int di; | ||
665 | int ch; | ||
666 | char *p; | ||
667 | |||
668 | skb = dev_alloc_skb(2); | ||
669 | if (!skb) { | ||
670 | printk(KERN_WARNING | ||
671 | "isdn_audio: Could not alloc skb for ttyI%d\n", | ||
672 | info->line); | ||
673 | return; | ||
674 | } | ||
675 | p = skb_put(skb, 2); | ||
676 | p[0] = 0x10; | ||
677 | p[1] = code; | ||
678 | ISDN_AUDIO_SKB_DLECOUNT(skb) = 0; | ||
679 | ISDN_AUDIO_SKB_LOCK(skb) = 0; | ||
680 | di = info->isdn_driver; | ||
681 | ch = info->isdn_channel; | ||
682 | __skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb); | ||
683 | dev->drv[di]->rcvcount[ch] += 2; | ||
684 | /* Schedule dequeuing */ | ||
685 | if ((dev->modempoll) && (info->rcvsched)) | ||
686 | isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); | ||
687 | wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]); | ||
688 | } | ||
689 | |||
690 | void | ||
691 | isdn_audio_eval_silence(modem_info *info) | ||
692 | { | ||
693 | silence_state *s = info->silence_state; | ||
694 | char what; | ||
695 | |||
696 | what = ' '; | ||
697 | |||
698 | if (s->idx > (info->emu.vpar[2] * 800)) { | ||
699 | s->idx = 0; | ||
700 | if (!s->state) { /* silence from beginning of rec */ | ||
701 | what = 's'; | ||
702 | } else { | ||
703 | what = 'q'; | ||
704 | } | ||
705 | } | ||
706 | if ((what == 's') || (what == 'q')) { | ||
707 | printk(KERN_DEBUG "ttyI%d: %s\n", info->line, | ||
708 | (what == 's') ? "silence" : "quiet"); | ||
709 | isdn_audio_put_dle_code(info, what); | ||
710 | } | ||
711 | } | ||
diff --git a/drivers/isdn/i4l/isdn_audio.h b/drivers/isdn/i4l/isdn_audio.h deleted file mode 100644 index 013c3582e0d1..000000000000 --- a/drivers/isdn/i4l/isdn_audio.h +++ /dev/null | |||
@@ -1,44 +0,0 @@ | |||
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 void isdn_audio_calc_dtmf(modem_info *, unsigned char *, int, int); | ||
39 | extern void isdn_audio_eval_dtmf(modem_info *); | ||
40 | dtmf_state *isdn_audio_dtmf_init(dtmf_state *); | ||
41 | extern void isdn_audio_calc_silence(modem_info *, unsigned char *, int, int); | ||
42 | extern void isdn_audio_eval_silence(modem_info *); | ||
43 | silence_state *isdn_audio_silence_init(silence_state *); | ||
44 | 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 deleted file mode 100644 index 7f28b967ed19..000000000000 --- a/drivers/isdn/i4l/isdn_bsdcomp.c +++ /dev/null | |||
@@ -1,930 +0,0 @@ | |||
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/types.h> | ||
60 | #include <linux/fcntl.h> | ||
61 | #include <linux/interrupt.h> | ||
62 | #include <linux/ptrace.h> | ||
63 | #include <linux/ioport.h> | ||
64 | #include <linux/in.h> | ||
65 | #include <linux/slab.h> | ||
66 | #include <linux/tty.h> | ||
67 | #include <linux/errno.h> | ||
68 | #include <linux/string.h> /* used in new tty drivers */ | ||
69 | #include <linux/signal.h> /* used in new tty drivers */ | ||
70 | #include <linux/bitops.h> | ||
71 | |||
72 | #include <asm/byteorder.h> | ||
73 | #include <asm/types.h> | ||
74 | |||
75 | #include <linux/if.h> | ||
76 | |||
77 | #include <linux/if_ether.h> | ||
78 | #include <linux/netdevice.h> | ||
79 | #include <linux/skbuff.h> | ||
80 | #include <linux/inet.h> | ||
81 | #include <linux/ioctl.h> | ||
82 | #include <linux/vmalloc.h> | ||
83 | |||
84 | #include <linux/ppp_defs.h> | ||
85 | |||
86 | #include <linux/isdn.h> | ||
87 | #include <linux/isdn_ppp.h> | ||
88 | #include <linux/ip.h> | ||
89 | #include <linux/tcp.h> | ||
90 | #include <linux/if_arp.h> | ||
91 | #include <linux/ppp-comp.h> | ||
92 | |||
93 | #include "isdn_ppp.h" | ||
94 | |||
95 | MODULE_DESCRIPTION("ISDN4Linux: BSD Compression for PPP over ISDN"); | ||
96 | MODULE_LICENSE("Dual BSD/GPL"); | ||
97 | |||
98 | #define BSD_VERSION(x) ((x) >> 5) | ||
99 | #define BSD_NBITS(x) ((x) & 0x1F) | ||
100 | |||
101 | #define BSD_CURRENT_VERSION 1 | ||
102 | |||
103 | #define DEBUG 1 | ||
104 | |||
105 | /* | ||
106 | * A dictionary for doing BSD compress. | ||
107 | */ | ||
108 | |||
109 | struct bsd_dict { | ||
110 | u32 fcode; | ||
111 | u16 codem1; /* output of hash table -1 */ | ||
112 | u16 cptr; /* map code to hash table entry */ | ||
113 | }; | ||
114 | |||
115 | struct bsd_db { | ||
116 | int totlen; /* length of this structure */ | ||
117 | unsigned int hsize; /* size of the hash table */ | ||
118 | unsigned char hshift; /* used in hash function */ | ||
119 | unsigned char n_bits; /* current bits/code */ | ||
120 | unsigned char maxbits; /* maximum bits/code */ | ||
121 | unsigned char debug; /* non-zero if debug desired */ | ||
122 | unsigned char unit; /* ppp unit number */ | ||
123 | u16 seqno; /* sequence # of next packet */ | ||
124 | unsigned int mru; /* size of receive (decompress) bufr */ | ||
125 | unsigned int maxmaxcode; /* largest valid code */ | ||
126 | unsigned int max_ent; /* largest code in use */ | ||
127 | unsigned int in_count; /* uncompressed bytes, aged */ | ||
128 | unsigned int bytes_out; /* compressed bytes, aged */ | ||
129 | unsigned int ratio; /* recent compression ratio */ | ||
130 | unsigned int checkpoint; /* when to next check the ratio */ | ||
131 | unsigned int clear_count; /* times dictionary cleared */ | ||
132 | unsigned int incomp_count; /* incompressible packets */ | ||
133 | unsigned int incomp_bytes; /* incompressible bytes */ | ||
134 | unsigned int uncomp_count; /* uncompressed packets */ | ||
135 | unsigned int uncomp_bytes; /* uncompressed bytes */ | ||
136 | unsigned int comp_count; /* compressed packets */ | ||
137 | unsigned int comp_bytes; /* compressed bytes */ | ||
138 | unsigned short *lens; /* array of lengths of codes */ | ||
139 | struct bsd_dict *dict; /* dictionary */ | ||
140 | int xmit; | ||
141 | }; | ||
142 | |||
143 | #define BSD_OVHD 2 /* BSD compress overhead/packet */ | ||
144 | #define MIN_BSD_BITS 9 | ||
145 | #define BSD_INIT_BITS MIN_BSD_BITS | ||
146 | #define MAX_BSD_BITS 15 | ||
147 | |||
148 | /* | ||
149 | * the next two codes should not be changed lightly, as they must not | ||
150 | * lie within the contiguous general code space. | ||
151 | */ | ||
152 | #define CLEAR 256 /* table clear output code */ | ||
153 | #define FIRST 257 /* first free entry */ | ||
154 | #define LAST 255 | ||
155 | |||
156 | #define MAXCODE(b) ((1 << (b)) - 1) | ||
157 | #define BADCODEM1 MAXCODE(MAX_BSD_BITS) | ||
158 | |||
159 | #define BSD_HASH(prefix, suffix, hshift) ((((unsigned long)(suffix)) << (hshift)) \ | ||
160 | ^ (unsigned long)(prefix)) | ||
161 | #define BSD_KEY(prefix, suffix) ((((unsigned long)(suffix)) << 16) \ | ||
162 | + (unsigned long)(prefix)) | ||
163 | |||
164 | #define CHECK_GAP 10000 /* Ratio check interval */ | ||
165 | |||
166 | #define RATIO_SCALE_LOG 8 | ||
167 | #define RATIO_SCALE (1 << RATIO_SCALE_LOG) | ||
168 | #define RATIO_MAX (0x7fffffff >> RATIO_SCALE_LOG) | ||
169 | |||
170 | /* | ||
171 | * clear the dictionary | ||
172 | */ | ||
173 | |||
174 | static void bsd_clear(struct bsd_db *db) | ||
175 | { | ||
176 | db->clear_count++; | ||
177 | db->max_ent = FIRST - 1; | ||
178 | db->n_bits = BSD_INIT_BITS; | ||
179 | db->bytes_out = 0; | ||
180 | db->in_count = 0; | ||
181 | db->incomp_count = 0; | ||
182 | db->ratio = 0; | ||
183 | db->checkpoint = CHECK_GAP; | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * If the dictionary is full, then see if it is time to reset it. | ||
188 | * | ||
189 | * Compute the compression ratio using fixed-point arithmetic | ||
190 | * with 8 fractional bits. | ||
191 | * | ||
192 | * Since we have an infinite stream instead of a single file, | ||
193 | * watch only the local compression ratio. | ||
194 | * | ||
195 | * Since both peers must reset the dictionary at the same time even in | ||
196 | * the absence of CLEAR codes (while packets are incompressible), they | ||
197 | * must compute the same ratio. | ||
198 | */ | ||
199 | static int bsd_check(struct bsd_db *db) /* 1=output CLEAR */ | ||
200 | { | ||
201 | unsigned int new_ratio; | ||
202 | |||
203 | if (db->in_count >= db->checkpoint) | ||
204 | { | ||
205 | /* age the ratio by limiting the size of the counts */ | ||
206 | if (db->in_count >= RATIO_MAX || db->bytes_out >= RATIO_MAX) | ||
207 | { | ||
208 | db->in_count -= (db->in_count >> 2); | ||
209 | db->bytes_out -= (db->bytes_out >> 2); | ||
210 | } | ||
211 | |||
212 | db->checkpoint = db->in_count + CHECK_GAP; | ||
213 | |||
214 | if (db->max_ent >= db->maxmaxcode) | ||
215 | { | ||
216 | /* Reset the dictionary only if the ratio is worse, | ||
217 | * or if it looks as if it has been poisoned | ||
218 | * by incompressible data. | ||
219 | * | ||
220 | * This does not overflow, because | ||
221 | * db->in_count <= RATIO_MAX. | ||
222 | */ | ||
223 | |||
224 | new_ratio = db->in_count << RATIO_SCALE_LOG; | ||
225 | if (db->bytes_out != 0) | ||
226 | { | ||
227 | new_ratio /= db->bytes_out; | ||
228 | } | ||
229 | |||
230 | if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE) | ||
231 | { | ||
232 | bsd_clear(db); | ||
233 | return 1; | ||
234 | } | ||
235 | db->ratio = new_ratio; | ||
236 | } | ||
237 | } | ||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | /* | ||
242 | * Return statistics. | ||
243 | */ | ||
244 | |||
245 | static void bsd_stats(void *state, struct compstat *stats) | ||
246 | { | ||
247 | struct bsd_db *db = (struct bsd_db *) state; | ||
248 | |||
249 | stats->unc_bytes = db->uncomp_bytes; | ||
250 | stats->unc_packets = db->uncomp_count; | ||
251 | stats->comp_bytes = db->comp_bytes; | ||
252 | stats->comp_packets = db->comp_count; | ||
253 | stats->inc_bytes = db->incomp_bytes; | ||
254 | stats->inc_packets = db->incomp_count; | ||
255 | stats->in_count = db->in_count; | ||
256 | stats->bytes_out = db->bytes_out; | ||
257 | } | ||
258 | |||
259 | /* | ||
260 | * Reset state, as on a CCP ResetReq. | ||
261 | */ | ||
262 | static void bsd_reset(void *state, unsigned char code, unsigned char id, | ||
263 | unsigned char *data, unsigned len, | ||
264 | struct isdn_ppp_resetparams *rsparm) | ||
265 | { | ||
266 | struct bsd_db *db = (struct bsd_db *) state; | ||
267 | |||
268 | bsd_clear(db); | ||
269 | db->seqno = 0; | ||
270 | db->clear_count = 0; | ||
271 | } | ||
272 | |||
273 | /* | ||
274 | * Release the compression structure | ||
275 | */ | ||
276 | static void bsd_free(void *state) | ||
277 | { | ||
278 | struct bsd_db *db = (struct bsd_db *) state; | ||
279 | |||
280 | if (db) { | ||
281 | /* | ||
282 | * Release the dictionary | ||
283 | */ | ||
284 | vfree(db->dict); | ||
285 | db->dict = NULL; | ||
286 | |||
287 | /* | ||
288 | * Release the string buffer | ||
289 | */ | ||
290 | vfree(db->lens); | ||
291 | db->lens = NULL; | ||
292 | |||
293 | /* | ||
294 | * Finally release the structure itself. | ||
295 | */ | ||
296 | kfree(db); | ||
297 | } | ||
298 | } | ||
299 | |||
300 | |||
301 | /* | ||
302 | * Allocate space for a (de) compressor. | ||
303 | */ | ||
304 | static void *bsd_alloc(struct isdn_ppp_comp_data *data) | ||
305 | { | ||
306 | int bits; | ||
307 | unsigned int hsize, hshift, maxmaxcode; | ||
308 | struct bsd_db *db; | ||
309 | int decomp; | ||
310 | |||
311 | static unsigned int htab[][2] = { | ||
312 | { 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } , | ||
313 | { 9001 , 5 } , { 18013 , 6 } , { 35023 , 7 } , { 69001 , 8 } | ||
314 | }; | ||
315 | |||
316 | if (data->optlen != 1 || data->num != CI_BSD_COMPRESS | ||
317 | || BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION) | ||
318 | return NULL; | ||
319 | |||
320 | bits = BSD_NBITS(data->options[0]); | ||
321 | |||
322 | if (bits < 9 || bits > 15) | ||
323 | return NULL; | ||
324 | |||
325 | hsize = htab[bits - 9][0]; | ||
326 | hshift = htab[bits - 9][1]; | ||
327 | |||
328 | /* | ||
329 | * Allocate the main control structure for this instance. | ||
330 | */ | ||
331 | maxmaxcode = MAXCODE(bits); | ||
332 | db = kzalloc(sizeof(struct bsd_db), GFP_KERNEL); | ||
333 | if (!db) | ||
334 | return NULL; | ||
335 | |||
336 | db->xmit = data->flags & IPPP_COMP_FLAG_XMIT; | ||
337 | decomp = db->xmit ? 0 : 1; | ||
338 | |||
339 | /* | ||
340 | * Allocate space for the dictionary. This may be more than one page in | ||
341 | * length. | ||
342 | */ | ||
343 | db->dict = vmalloc(array_size(hsize, sizeof(struct bsd_dict))); | ||
344 | if (!db->dict) { | ||
345 | bsd_free(db); | ||
346 | return NULL; | ||
347 | } | ||
348 | |||
349 | /* | ||
350 | * If this is the compression buffer then there is no length data. | ||
351 | * For decompression, the length information is needed as well. | ||
352 | */ | ||
353 | if (!decomp) | ||
354 | db->lens = NULL; | ||
355 | else { | ||
356 | db->lens = vmalloc(array_size(sizeof(db->lens[0]), | ||
357 | maxmaxcode + 1)); | ||
358 | if (!db->lens) { | ||
359 | bsd_free(db); | ||
360 | return (NULL); | ||
361 | } | ||
362 | } | ||
363 | |||
364 | /* | ||
365 | * Initialize the data information for the compression code | ||
366 | */ | ||
367 | db->totlen = sizeof(struct bsd_db) + (sizeof(struct bsd_dict) * hsize); | ||
368 | db->hsize = hsize; | ||
369 | db->hshift = hshift; | ||
370 | db->maxmaxcode = maxmaxcode; | ||
371 | db->maxbits = bits; | ||
372 | |||
373 | return (void *)db; | ||
374 | } | ||
375 | |||
376 | /* | ||
377 | * Initialize the database. | ||
378 | */ | ||
379 | static int bsd_init(void *state, struct isdn_ppp_comp_data *data, int unit, int debug) | ||
380 | { | ||
381 | struct bsd_db *db = state; | ||
382 | int indx; | ||
383 | int decomp; | ||
384 | |||
385 | if (!state || !data) { | ||
386 | printk(KERN_ERR "isdn_bsd_init: [%d] ERR, state %lx data %lx\n", unit, (long)state, (long)data); | ||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | decomp = db->xmit ? 0 : 1; | ||
391 | |||
392 | if (data->optlen != 1 || data->num != CI_BSD_COMPRESS | ||
393 | || (BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION) | ||
394 | || (BSD_NBITS(data->options[0]) != db->maxbits) | ||
395 | || (decomp && db->lens == NULL)) { | ||
396 | printk(KERN_ERR "isdn_bsd: %d %d %d %d %lx\n", data->optlen, data->num, data->options[0], decomp, (unsigned long)db->lens); | ||
397 | return 0; | ||
398 | } | ||
399 | |||
400 | if (decomp) | ||
401 | for (indx = LAST; indx >= 0; indx--) | ||
402 | db->lens[indx] = 1; | ||
403 | |||
404 | indx = db->hsize; | ||
405 | while (indx-- != 0) { | ||
406 | db->dict[indx].codem1 = BADCODEM1; | ||
407 | db->dict[indx].cptr = 0; | ||
408 | } | ||
409 | |||
410 | db->unit = unit; | ||
411 | db->mru = 0; | ||
412 | |||
413 | db->debug = 1; | ||
414 | |||
415 | bsd_reset(db, 0, 0, NULL, 0, NULL); | ||
416 | |||
417 | return 1; | ||
418 | } | ||
419 | |||
420 | /* | ||
421 | * Obtain pointers to the various structures in the compression tables | ||
422 | */ | ||
423 | |||
424 | #define dict_ptrx(p, idx) &(p->dict[idx]) | ||
425 | #define lens_ptrx(p, idx) &(p->lens[idx]) | ||
426 | |||
427 | #ifdef DEBUG | ||
428 | static unsigned short *lens_ptr(struct bsd_db *db, int idx) | ||
429 | { | ||
430 | if ((unsigned int) idx > (unsigned int) db->maxmaxcode) { | ||
431 | printk(KERN_DEBUG "<9>ppp: lens_ptr(%d) > max\n", idx); | ||
432 | idx = 0; | ||
433 | } | ||
434 | return lens_ptrx(db, idx); | ||
435 | } | ||
436 | |||
437 | static struct bsd_dict *dict_ptr(struct bsd_db *db, int idx) | ||
438 | { | ||
439 | if ((unsigned int) idx >= (unsigned int) db->hsize) { | ||
440 | printk(KERN_DEBUG "<9>ppp: dict_ptr(%d) > max\n", idx); | ||
441 | idx = 0; | ||
442 | } | ||
443 | return dict_ptrx(db, idx); | ||
444 | } | ||
445 | |||
446 | #else | ||
447 | #define lens_ptr(db, idx) lens_ptrx(db, idx) | ||
448 | #define dict_ptr(db, idx) dict_ptrx(db, idx) | ||
449 | #endif | ||
450 | |||
451 | /* | ||
452 | * compress a packet | ||
453 | */ | ||
454 | static int bsd_compress(void *state, struct sk_buff *skb_in, struct sk_buff *skb_out, int proto) | ||
455 | { | ||
456 | struct bsd_db *db; | ||
457 | int hshift; | ||
458 | unsigned int max_ent; | ||
459 | unsigned int n_bits; | ||
460 | unsigned int bitno; | ||
461 | unsigned long accm; | ||
462 | int ent; | ||
463 | unsigned long fcode; | ||
464 | struct bsd_dict *dictp; | ||
465 | unsigned char c; | ||
466 | int hval, disp, ilen, mxcode; | ||
467 | unsigned char *rptr = skb_in->data; | ||
468 | int isize = skb_in->len; | ||
469 | |||
470 | #define OUTPUT(ent) \ | ||
471 | { \ | ||
472 | bitno -= n_bits; \ | ||
473 | accm |= ((ent) << bitno); \ | ||
474 | do { \ | ||
475 | if (skb_out && skb_tailroom(skb_out) > 0) \ | ||
476 | skb_put_u8(skb_out, (u8)(accm >> 24)); \ | ||
477 | accm <<= 8; \ | ||
478 | bitno += 8; \ | ||
479 | } while (bitno <= 24); \ | ||
480 | } | ||
481 | |||
482 | /* | ||
483 | * If the protocol is not in the range we're interested in, | ||
484 | * just return without compressing the packet. If it is, | ||
485 | * the protocol becomes the first byte to compress. | ||
486 | */ | ||
487 | printk(KERN_DEBUG "bsd_compress called with %x\n", proto); | ||
488 | |||
489 | ent = proto; | ||
490 | if (proto < 0x21 || proto > 0xf9 || !(proto & 0x1)) | ||
491 | return 0; | ||
492 | |||
493 | db = (struct bsd_db *) state; | ||
494 | hshift = db->hshift; | ||
495 | max_ent = db->max_ent; | ||
496 | n_bits = db->n_bits; | ||
497 | bitno = 32; | ||
498 | accm = 0; | ||
499 | mxcode = MAXCODE(n_bits); | ||
500 | |||
501 | /* This is the PPP header information */ | ||
502 | if (skb_out && skb_tailroom(skb_out) >= 2) { | ||
503 | char *v = skb_put(skb_out, 2); | ||
504 | /* we only push our own data on the header, | ||
505 | AC,PC and protos is pushed by caller */ | ||
506 | v[0] = db->seqno >> 8; | ||
507 | v[1] = db->seqno; | ||
508 | } | ||
509 | |||
510 | ilen = ++isize; /* This is off by one, but that is what is in draft! */ | ||
511 | |||
512 | while (--ilen > 0) { | ||
513 | c = *rptr++; | ||
514 | fcode = BSD_KEY(ent, c); | ||
515 | hval = BSD_HASH(ent, c, hshift); | ||
516 | dictp = dict_ptr(db, hval); | ||
517 | |||
518 | /* Validate and then check the entry. */ | ||
519 | if (dictp->codem1 >= max_ent) | ||
520 | goto nomatch; | ||
521 | |||
522 | if (dictp->fcode == fcode) { | ||
523 | ent = dictp->codem1 + 1; | ||
524 | continue; /* found (prefix,suffix) */ | ||
525 | } | ||
526 | |||
527 | /* continue probing until a match or invalid entry */ | ||
528 | disp = (hval == 0) ? 1 : hval; | ||
529 | |||
530 | do { | ||
531 | hval += disp; | ||
532 | if (hval >= db->hsize) | ||
533 | hval -= db->hsize; | ||
534 | dictp = dict_ptr(db, hval); | ||
535 | if (dictp->codem1 >= max_ent) | ||
536 | goto nomatch; | ||
537 | } while (dictp->fcode != fcode); | ||
538 | |||
539 | ent = dictp->codem1 + 1; /* finally found (prefix,suffix) */ | ||
540 | continue; | ||
541 | |||
542 | nomatch: | ||
543 | OUTPUT(ent); /* output the prefix */ | ||
544 | |||
545 | /* code -> hashtable */ | ||
546 | if (max_ent < db->maxmaxcode) { | ||
547 | struct bsd_dict *dictp2; | ||
548 | struct bsd_dict *dictp3; | ||
549 | int indx; | ||
550 | |||
551 | /* expand code size if needed */ | ||
552 | if (max_ent >= mxcode) { | ||
553 | db->n_bits = ++n_bits; | ||
554 | mxcode = MAXCODE(n_bits); | ||
555 | } | ||
556 | |||
557 | /* | ||
558 | * Invalidate old hash table entry using | ||
559 | * this code, and then take it over. | ||
560 | */ | ||
561 | dictp2 = dict_ptr(db, max_ent + 1); | ||
562 | indx = dictp2->cptr; | ||
563 | dictp3 = dict_ptr(db, indx); | ||
564 | |||
565 | if (dictp3->codem1 == max_ent) | ||
566 | dictp3->codem1 = BADCODEM1; | ||
567 | |||
568 | dictp2->cptr = hval; | ||
569 | dictp->codem1 = max_ent; | ||
570 | dictp->fcode = fcode; | ||
571 | db->max_ent = ++max_ent; | ||
572 | |||
573 | if (db->lens) { | ||
574 | unsigned short *len1 = lens_ptr(db, max_ent); | ||
575 | unsigned short *len2 = lens_ptr(db, ent); | ||
576 | *len1 = *len2 + 1; | ||
577 | } | ||
578 | } | ||
579 | ent = c; | ||
580 | } | ||
581 | |||
582 | OUTPUT(ent); /* output the last code */ | ||
583 | |||
584 | if (skb_out) | ||
585 | db->bytes_out += skb_out->len; /* Do not count bytes from here */ | ||
586 | db->uncomp_bytes += isize; | ||
587 | db->in_count += isize; | ||
588 | ++db->uncomp_count; | ||
589 | ++db->seqno; | ||
590 | |||
591 | if (bitno < 32) | ||
592 | ++db->bytes_out; /* must be set before calling bsd_check */ | ||
593 | |||
594 | /* | ||
595 | * Generate the clear command if needed | ||
596 | */ | ||
597 | |||
598 | if (bsd_check(db)) | ||
599 | OUTPUT(CLEAR); | ||
600 | |||
601 | /* | ||
602 | * Pad dribble bits of last code with ones. | ||
603 | * Do not emit a completely useless byte of ones. | ||
604 | */ | ||
605 | if (bitno < 32 && skb_out && skb_tailroom(skb_out) > 0) | ||
606 | skb_put_u8(skb_out, | ||
607 | (unsigned char)((accm | (0xff << (bitno - 8))) >> 24)); | ||
608 | |||
609 | /* | ||
610 | * Increase code size if we would have without the packet | ||
611 | * boundary because the decompressor will do so. | ||
612 | */ | ||
613 | if (max_ent >= mxcode && max_ent < db->maxmaxcode) | ||
614 | db->n_bits++; | ||
615 | |||
616 | /* If output length is too large then this is an incompressible frame. */ | ||
617 | if (!skb_out || skb_out->len >= skb_in->len) { | ||
618 | ++db->incomp_count; | ||
619 | db->incomp_bytes += isize; | ||
620 | return 0; | ||
621 | } | ||
622 | |||
623 | /* Count the number of compressed frames */ | ||
624 | ++db->comp_count; | ||
625 | db->comp_bytes += skb_out->len; | ||
626 | return skb_out->len; | ||
627 | |||
628 | #undef OUTPUT | ||
629 | } | ||
630 | |||
631 | /* | ||
632 | * Update the "BSD Compress" dictionary on the receiver for | ||
633 | * incompressible data by pretending to compress the incoming data. | ||
634 | */ | ||
635 | static void bsd_incomp(void *state, struct sk_buff *skb_in, int proto) | ||
636 | { | ||
637 | bsd_compress(state, skb_in, NULL, proto); | ||
638 | } | ||
639 | |||
640 | /* | ||
641 | * Decompress "BSD Compress". | ||
642 | */ | ||
643 | static int bsd_decompress(void *state, struct sk_buff *skb_in, struct sk_buff *skb_out, | ||
644 | struct isdn_ppp_resetparams *rsparm) | ||
645 | { | ||
646 | struct bsd_db *db; | ||
647 | unsigned int max_ent; | ||
648 | unsigned long accm; | ||
649 | unsigned int bitno; /* 1st valid bit in accm */ | ||
650 | unsigned int n_bits; | ||
651 | unsigned int tgtbitno; /* bitno when we have a code */ | ||
652 | struct bsd_dict *dictp; | ||
653 | int seq; | ||
654 | unsigned int incode; | ||
655 | unsigned int oldcode; | ||
656 | unsigned int finchar; | ||
657 | unsigned char *p, *ibuf; | ||
658 | int ilen; | ||
659 | int codelen; | ||
660 | int extra; | ||
661 | |||
662 | db = (struct bsd_db *) state; | ||
663 | max_ent = db->max_ent; | ||
664 | accm = 0; | ||
665 | bitno = 32; /* 1st valid bit in accm */ | ||
666 | n_bits = db->n_bits; | ||
667 | tgtbitno = 32 - n_bits; /* bitno when we have a code */ | ||
668 | |||
669 | printk(KERN_DEBUG "bsd_decompress called\n"); | ||
670 | |||
671 | if (!skb_in || !skb_out) { | ||
672 | printk(KERN_ERR "bsd_decompress called with NULL parameter\n"); | ||
673 | return DECOMP_ERROR; | ||
674 | } | ||
675 | |||
676 | /* | ||
677 | * Get the sequence number. | ||
678 | */ | ||
679 | if ((p = skb_pull(skb_in, 2)) == NULL) { | ||
680 | return DECOMP_ERROR; | ||
681 | } | ||
682 | p -= 2; | ||
683 | seq = (p[0] << 8) + p[1]; | ||
684 | ilen = skb_in->len; | ||
685 | ibuf = skb_in->data; | ||
686 | |||
687 | /* | ||
688 | * Check the sequence number and give up if it differs from | ||
689 | * the value we're expecting. | ||
690 | */ | ||
691 | if (seq != db->seqno) { | ||
692 | if (db->debug) { | ||
693 | printk(KERN_DEBUG "bsd_decomp%d: bad sequence # %d, expected %d\n", | ||
694 | db->unit, seq, db->seqno - 1); | ||
695 | } | ||
696 | return DECOMP_ERROR; | ||
697 | } | ||
698 | |||
699 | ++db->seqno; | ||
700 | db->bytes_out += ilen; | ||
701 | |||
702 | if (skb_tailroom(skb_out) > 0) | ||
703 | skb_put_u8(skb_out, 0); | ||
704 | else | ||
705 | return DECOMP_ERR_NOMEM; | ||
706 | |||
707 | oldcode = CLEAR; | ||
708 | |||
709 | /* | ||
710 | * Keep the checkpoint correctly so that incompressible packets | ||
711 | * clear the dictionary at the proper times. | ||
712 | */ | ||
713 | |||
714 | for (;;) { | ||
715 | if (ilen-- <= 0) { | ||
716 | db->in_count += (skb_out->len - 1); /* don't count the header */ | ||
717 | break; | ||
718 | } | ||
719 | |||
720 | /* | ||
721 | * Accumulate bytes until we have a complete code. | ||
722 | * Then get the next code, relying on the 32-bit, | ||
723 | * unsigned accm to mask the result. | ||
724 | */ | ||
725 | |||
726 | bitno -= 8; | ||
727 | accm |= *ibuf++ << bitno; | ||
728 | if (tgtbitno < bitno) | ||
729 | continue; | ||
730 | |||
731 | incode = accm >> tgtbitno; | ||
732 | accm <<= n_bits; | ||
733 | bitno += n_bits; | ||
734 | |||
735 | /* | ||
736 | * The dictionary must only be cleared at the end of a packet. | ||
737 | */ | ||
738 | |||
739 | if (incode == CLEAR) { | ||
740 | if (ilen > 0) { | ||
741 | if (db->debug) | ||
742 | printk(KERN_DEBUG "bsd_decomp%d: bad CLEAR\n", db->unit); | ||
743 | return DECOMP_FATALERROR; /* probably a bug */ | ||
744 | } | ||
745 | bsd_clear(db); | ||
746 | break; | ||
747 | } | ||
748 | |||
749 | if ((incode > max_ent + 2) || (incode > db->maxmaxcode) | ||
750 | || (incode > max_ent && oldcode == CLEAR)) { | ||
751 | if (db->debug) { | ||
752 | printk(KERN_DEBUG "bsd_decomp%d: bad code 0x%x oldcode=0x%x ", | ||
753 | db->unit, incode, oldcode); | ||
754 | printk(KERN_DEBUG "max_ent=0x%x skb->Len=%d seqno=%d\n", | ||
755 | max_ent, skb_out->len, db->seqno); | ||
756 | } | ||
757 | return DECOMP_FATALERROR; /* probably a bug */ | ||
758 | } | ||
759 | |||
760 | /* Special case for KwKwK string. */ | ||
761 | if (incode > max_ent) { | ||
762 | finchar = oldcode; | ||
763 | extra = 1; | ||
764 | } else { | ||
765 | finchar = incode; | ||
766 | extra = 0; | ||
767 | } | ||
768 | |||
769 | codelen = *(lens_ptr(db, finchar)); | ||
770 | if (skb_tailroom(skb_out) < codelen + extra) { | ||
771 | if (db->debug) { | ||
772 | printk(KERN_DEBUG "bsd_decomp%d: ran out of mru\n", db->unit); | ||
773 | #ifdef DEBUG | ||
774 | printk(KERN_DEBUG " len=%d, finchar=0x%x, codelen=%d,skblen=%d\n", | ||
775 | ilen, finchar, codelen, skb_out->len); | ||
776 | #endif | ||
777 | } | ||
778 | return DECOMP_FATALERROR; | ||
779 | } | ||
780 | |||
781 | /* | ||
782 | * Decode this code and install it in the decompressed buffer. | ||
783 | */ | ||
784 | |||
785 | p = skb_put(skb_out, codelen); | ||
786 | p += codelen; | ||
787 | while (finchar > LAST) { | ||
788 | struct bsd_dict *dictp2 = dict_ptr(db, finchar); | ||
789 | |||
790 | dictp = dict_ptr(db, dictp2->cptr); | ||
791 | |||
792 | #ifdef DEBUG | ||
793 | if (--codelen <= 0 || dictp->codem1 != finchar - 1) { | ||
794 | if (codelen <= 0) { | ||
795 | printk(KERN_ERR "bsd_decomp%d: fell off end of chain ", db->unit); | ||
796 | printk(KERN_ERR "0x%x at 0x%x by 0x%x, max_ent=0x%x\n", incode, finchar, dictp2->cptr, max_ent); | ||
797 | } else { | ||
798 | if (dictp->codem1 != finchar - 1) { | ||
799 | printk(KERN_ERR "bsd_decomp%d: bad code chain 0x%x finchar=0x%x ", db->unit, incode, finchar); | ||
800 | printk(KERN_ERR "oldcode=0x%x cptr=0x%x codem1=0x%x\n", oldcode, dictp2->cptr, dictp->codem1); | ||
801 | } | ||
802 | } | ||
803 | return DECOMP_FATALERROR; | ||
804 | } | ||
805 | #endif | ||
806 | |||
807 | { | ||
808 | u32 fcode = dictp->fcode; | ||
809 | *--p = (fcode >> 16) & 0xff; | ||
810 | finchar = fcode & 0xffff; | ||
811 | } | ||
812 | } | ||
813 | *--p = finchar; | ||
814 | |||
815 | #ifdef DEBUG | ||
816 | if (--codelen != 0) | ||
817 | printk(KERN_ERR "bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n", db->unit, codelen, incode, max_ent); | ||
818 | #endif | ||
819 | |||
820 | if (extra) /* the KwKwK case again */ | ||
821 | skb_put_u8(skb_out, finchar); | ||
822 | |||
823 | /* | ||
824 | * If not first code in a packet, and | ||
825 | * if not out of code space, then allocate a new code. | ||
826 | * | ||
827 | * Keep the hash table correct so it can be used | ||
828 | * with uncompressed packets. | ||
829 | */ | ||
830 | if (oldcode != CLEAR && max_ent < db->maxmaxcode) { | ||
831 | struct bsd_dict *dictp2, *dictp3; | ||
832 | u16 *lens1, *lens2; | ||
833 | unsigned long fcode; | ||
834 | int hval, disp, indx; | ||
835 | |||
836 | fcode = BSD_KEY(oldcode, finchar); | ||
837 | hval = BSD_HASH(oldcode, finchar, db->hshift); | ||
838 | dictp = dict_ptr(db, hval); | ||
839 | |||
840 | /* look for a free hash table entry */ | ||
841 | if (dictp->codem1 < max_ent) { | ||
842 | disp = (hval == 0) ? 1 : hval; | ||
843 | do { | ||
844 | hval += disp; | ||
845 | if (hval >= db->hsize) | ||
846 | hval -= db->hsize; | ||
847 | dictp = dict_ptr(db, hval); | ||
848 | } while (dictp->codem1 < max_ent); | ||
849 | } | ||
850 | |||
851 | /* | ||
852 | * Invalidate previous hash table entry | ||
853 | * assigned this code, and then take it over | ||
854 | */ | ||
855 | |||
856 | dictp2 = dict_ptr(db, max_ent + 1); | ||
857 | indx = dictp2->cptr; | ||
858 | dictp3 = dict_ptr(db, indx); | ||
859 | |||
860 | if (dictp3->codem1 == max_ent) | ||
861 | dictp3->codem1 = BADCODEM1; | ||
862 | |||
863 | dictp2->cptr = hval; | ||
864 | dictp->codem1 = max_ent; | ||
865 | dictp->fcode = fcode; | ||
866 | db->max_ent = ++max_ent; | ||
867 | |||
868 | /* Update the length of this string. */ | ||
869 | lens1 = lens_ptr(db, max_ent); | ||
870 | lens2 = lens_ptr(db, oldcode); | ||
871 | *lens1 = *lens2 + 1; | ||
872 | |||
873 | /* Expand code size if needed. */ | ||
874 | if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) { | ||
875 | db->n_bits = ++n_bits; | ||
876 | tgtbitno = 32-n_bits; | ||
877 | } | ||
878 | } | ||
879 | oldcode = incode; | ||
880 | } | ||
881 | |||
882 | ++db->comp_count; | ||
883 | ++db->uncomp_count; | ||
884 | db->comp_bytes += skb_in->len - BSD_OVHD; | ||
885 | db->uncomp_bytes += skb_out->len; | ||
886 | |||
887 | if (bsd_check(db)) { | ||
888 | if (db->debug) | ||
889 | printk(KERN_DEBUG "bsd_decomp%d: peer should have cleared dictionary on %d\n", | ||
890 | db->unit, db->seqno - 1); | ||
891 | } | ||
892 | return skb_out->len; | ||
893 | } | ||
894 | |||
895 | /************************************************************* | ||
896 | * Table of addresses for the BSD compression module | ||
897 | *************************************************************/ | ||
898 | |||
899 | static struct isdn_ppp_compressor ippp_bsd_compress = { | ||
900 | .owner = THIS_MODULE, | ||
901 | .num = CI_BSD_COMPRESS, | ||
902 | .alloc = bsd_alloc, | ||
903 | .free = bsd_free, | ||
904 | .init = bsd_init, | ||
905 | .reset = bsd_reset, | ||
906 | .compress = bsd_compress, | ||
907 | .decompress = bsd_decompress, | ||
908 | .incomp = bsd_incomp, | ||
909 | .stat = bsd_stats, | ||
910 | }; | ||
911 | |||
912 | /************************************************************* | ||
913 | * Module support routines | ||
914 | *************************************************************/ | ||
915 | |||
916 | static int __init isdn_bsdcomp_init(void) | ||
917 | { | ||
918 | int answer = isdn_ppp_register_compressor(&ippp_bsd_compress); | ||
919 | if (answer == 0) | ||
920 | printk(KERN_INFO "PPP BSD Compression module registered\n"); | ||
921 | return answer; | ||
922 | } | ||
923 | |||
924 | static void __exit isdn_bsdcomp_exit(void) | ||
925 | { | ||
926 | isdn_ppp_unregister_compressor(&ippp_bsd_compress); | ||
927 | } | ||
928 | |||
929 | module_init(isdn_bsdcomp_init); | ||
930 | module_exit(isdn_bsdcomp_exit); | ||
diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c deleted file mode 100644 index 74ee00f5b310..000000000000 --- a/drivers/isdn/i4l/isdn_common.c +++ /dev/null | |||
@@ -1,2368 +0,0 @@ | |||
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/module.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/poll.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/vmalloc.h> | ||
19 | #include <linux/isdn.h> | ||
20 | #include <linux/mutex.h> | ||
21 | #include "isdn_common.h" | ||
22 | #include "isdn_tty.h" | ||
23 | #include "isdn_net.h" | ||
24 | #include "isdn_ppp.h" | ||
25 | #ifdef CONFIG_ISDN_AUDIO | ||
26 | #include "isdn_audio.h" | ||
27 | #endif | ||
28 | #ifdef CONFIG_ISDN_DIVERSION_MODULE | ||
29 | #define CONFIG_ISDN_DIVERSION | ||
30 | #endif | ||
31 | #ifdef CONFIG_ISDN_DIVERSION | ||
32 | #include <linux/isdn_divertif.h> | ||
33 | #endif /* CONFIG_ISDN_DIVERSION */ | ||
34 | #include "isdn_v110.h" | ||
35 | |||
36 | /* Debugflags */ | ||
37 | #undef ISDN_DEBUG_STATCALLB | ||
38 | |||
39 | MODULE_DESCRIPTION("ISDN4Linux: link layer"); | ||
40 | MODULE_AUTHOR("Fritz Elfert"); | ||
41 | MODULE_LICENSE("GPL"); | ||
42 | |||
43 | isdn_dev *dev; | ||
44 | |||
45 | static DEFINE_MUTEX(isdn_mutex); | ||
46 | static char *isdn_revision = "$Revision: 1.1.2.3 $"; | ||
47 | |||
48 | extern char *isdn_net_revision; | ||
49 | #ifdef CONFIG_ISDN_PPP | ||
50 | extern char *isdn_ppp_revision; | ||
51 | #else | ||
52 | static char *isdn_ppp_revision = ": none $"; | ||
53 | #endif | ||
54 | #ifdef CONFIG_ISDN_AUDIO | ||
55 | extern char *isdn_audio_revision; | ||
56 | #else | ||
57 | static char *isdn_audio_revision = ": none $"; | ||
58 | #endif | ||
59 | extern char *isdn_v110_revision; | ||
60 | |||
61 | #ifdef CONFIG_ISDN_DIVERSION | ||
62 | static isdn_divert_if *divert_if; /* = NULL */ | ||
63 | #endif /* CONFIG_ISDN_DIVERSION */ | ||
64 | |||
65 | |||
66 | static int isdn_writebuf_stub(int, int, const u_char __user *, int); | ||
67 | static void set_global_features(void); | ||
68 | static int isdn_wildmat(char *s, char *p); | ||
69 | static int isdn_add_channels(isdn_driver_t *d, int drvidx, int n, int adding); | ||
70 | |||
71 | static inline void | ||
72 | isdn_lock_driver(isdn_driver_t *drv) | ||
73 | { | ||
74 | try_module_get(drv->interface->owner); | ||
75 | drv->locks++; | ||
76 | } | ||
77 | |||
78 | void | ||
79 | isdn_lock_drivers(void) | ||
80 | { | ||
81 | int i; | ||
82 | |||
83 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) { | ||
84 | if (!dev->drv[i]) | ||
85 | continue; | ||
86 | isdn_lock_driver(dev->drv[i]); | ||
87 | } | ||
88 | } | ||
89 | |||
90 | static inline void | ||
91 | isdn_unlock_driver(isdn_driver_t *drv) | ||
92 | { | ||
93 | if (drv->locks > 0) { | ||
94 | drv->locks--; | ||
95 | module_put(drv->interface->owner); | ||
96 | } | ||
97 | } | ||
98 | |||
99 | void | ||
100 | isdn_unlock_drivers(void) | ||
101 | { | ||
102 | int i; | ||
103 | |||
104 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) { | ||
105 | if (!dev->drv[i]) | ||
106 | continue; | ||
107 | isdn_unlock_driver(dev->drv[i]); | ||
108 | } | ||
109 | } | ||
110 | |||
111 | #if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP) | ||
112 | void | ||
113 | isdn_dumppkt(char *s, u_char *p, int len, int dumplen) | ||
114 | { | ||
115 | int dumpc; | ||
116 | |||
117 | printk(KERN_DEBUG "%s(%d) ", s, len); | ||
118 | for (dumpc = 0; (dumpc < dumplen) && (len); len--, dumpc++) | ||
119 | printk(" %02x", *p++); | ||
120 | printk("\n"); | ||
121 | } | ||
122 | #endif | ||
123 | |||
124 | /* | ||
125 | * I picked the pattern-matching-functions from an old GNU-tar version (1.10) | ||
126 | * It was originally written and put to PD by rs@mirror.TMC.COM (Rich Salz) | ||
127 | */ | ||
128 | static int | ||
129 | isdn_star(char *s, char *p) | ||
130 | { | ||
131 | while (isdn_wildmat(s, p)) { | ||
132 | if (*++s == '\0') | ||
133 | return (2); | ||
134 | } | ||
135 | return (0); | ||
136 | } | ||
137 | |||
138 | /* | ||
139 | * Shell-type Pattern-matching for incoming caller-Ids | ||
140 | * This function gets a string in s and checks, if it matches the pattern | ||
141 | * given in p. | ||
142 | * | ||
143 | * Return: | ||
144 | * 0 = match. | ||
145 | * 1 = no match. | ||
146 | * 2 = no match. Would eventually match, if s would be longer. | ||
147 | * | ||
148 | * Possible Patterns: | ||
149 | * | ||
150 | * '?' matches one character | ||
151 | * '*' matches zero or more characters | ||
152 | * [xyz] matches the set of characters in brackets. | ||
153 | * [^xyz] matches any single character not in the set of characters | ||
154 | */ | ||
155 | |||
156 | static int | ||
157 | isdn_wildmat(char *s, char *p) | ||
158 | { | ||
159 | register int last; | ||
160 | register int matched; | ||
161 | register int reverse; | ||
162 | register int nostar = 1; | ||
163 | |||
164 | if (!(*s) && !(*p)) | ||
165 | return (1); | ||
166 | for (; *p; s++, p++) | ||
167 | switch (*p) { | ||
168 | case '\\': | ||
169 | /* Literal match with following character. */ | ||
170 | p++; | ||
171 | /* fall through */ | ||
172 | default: | ||
173 | if (*s != *p) | ||
174 | return (*s == '\0') ? 2 : 1; | ||
175 | continue; | ||
176 | case '?': | ||
177 | /* Match anything. */ | ||
178 | if (*s == '\0') | ||
179 | return (2); | ||
180 | continue; | ||
181 | case '*': | ||
182 | nostar = 0; | ||
183 | /* Trailing star matches everything. */ | ||
184 | return (*++p ? isdn_star(s, p) : 0); | ||
185 | case '[': | ||
186 | /* [^....] means inverse character class. */ | ||
187 | if ((reverse = (p[1] == '^'))) | ||
188 | p++; | ||
189 | for (last = 0, matched = 0; *++p && (*p != ']'); last = *p) | ||
190 | /* This next line requires a good C compiler. */ | ||
191 | if (*p == '-' ? *s <= *++p && *s >= last : *s == *p) | ||
192 | matched = 1; | ||
193 | if (matched == reverse) | ||
194 | return (1); | ||
195 | continue; | ||
196 | } | ||
197 | return (*s == '\0') ? 0 : nostar; | ||
198 | } | ||
199 | |||
200 | int isdn_msncmp(const char *msn1, const char *msn2) | ||
201 | { | ||
202 | char TmpMsn1[ISDN_MSNLEN]; | ||
203 | char TmpMsn2[ISDN_MSNLEN]; | ||
204 | char *p; | ||
205 | |||
206 | for (p = TmpMsn1; *msn1 && *msn1 != ':';) // Strip off a SPID | ||
207 | *p++ = *msn1++; | ||
208 | *p = '\0'; | ||
209 | |||
210 | for (p = TmpMsn2; *msn2 && *msn2 != ':';) // Strip off a SPID | ||
211 | *p++ = *msn2++; | ||
212 | *p = '\0'; | ||
213 | |||
214 | return isdn_wildmat(TmpMsn1, TmpMsn2); | ||
215 | } | ||
216 | |||
217 | int | ||
218 | isdn_dc2minor(int di, int ch) | ||
219 | { | ||
220 | int i; | ||
221 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
222 | if (dev->chanmap[i] == ch && dev->drvmap[i] == di) | ||
223 | return i; | ||
224 | return -1; | ||
225 | } | ||
226 | |||
227 | static int isdn_timer_cnt1 = 0; | ||
228 | static int isdn_timer_cnt2 = 0; | ||
229 | static int isdn_timer_cnt3 = 0; | ||
230 | |||
231 | static void | ||
232 | isdn_timer_funct(struct timer_list *unused) | ||
233 | { | ||
234 | int tf = dev->tflags; | ||
235 | if (tf & ISDN_TIMER_FAST) { | ||
236 | if (tf & ISDN_TIMER_MODEMREAD) | ||
237 | isdn_tty_readmodem(); | ||
238 | if (tf & ISDN_TIMER_MODEMPLUS) | ||
239 | isdn_tty_modem_escape(); | ||
240 | if (tf & ISDN_TIMER_MODEMXMIT) | ||
241 | isdn_tty_modem_xmit(); | ||
242 | } | ||
243 | if (tf & ISDN_TIMER_SLOW) { | ||
244 | if (++isdn_timer_cnt1 >= ISDN_TIMER_02SEC) { | ||
245 | isdn_timer_cnt1 = 0; | ||
246 | if (tf & ISDN_TIMER_NETDIAL) | ||
247 | isdn_net_dial(); | ||
248 | } | ||
249 | if (++isdn_timer_cnt2 >= ISDN_TIMER_1SEC) { | ||
250 | isdn_timer_cnt2 = 0; | ||
251 | if (tf & ISDN_TIMER_NETHANGUP) | ||
252 | isdn_net_autohup(); | ||
253 | if (++isdn_timer_cnt3 >= ISDN_TIMER_RINGING) { | ||
254 | isdn_timer_cnt3 = 0; | ||
255 | if (tf & ISDN_TIMER_MODEMRING) | ||
256 | isdn_tty_modem_ring(); | ||
257 | } | ||
258 | if (tf & ISDN_TIMER_CARRIER) | ||
259 | isdn_tty_carrier_timeout(); | ||
260 | } | ||
261 | } | ||
262 | if (tf) | ||
263 | mod_timer(&dev->timer, jiffies + ISDN_TIMER_RES); | ||
264 | } | ||
265 | |||
266 | void | ||
267 | isdn_timer_ctrl(int tf, int onoff) | ||
268 | { | ||
269 | unsigned long flags; | ||
270 | int old_tflags; | ||
271 | |||
272 | spin_lock_irqsave(&dev->timerlock, flags); | ||
273 | if ((tf & ISDN_TIMER_SLOW) && (!(dev->tflags & ISDN_TIMER_SLOW))) { | ||
274 | /* If the slow-timer wasn't activated until now */ | ||
275 | isdn_timer_cnt1 = 0; | ||
276 | isdn_timer_cnt2 = 0; | ||
277 | } | ||
278 | old_tflags = dev->tflags; | ||
279 | if (onoff) | ||
280 | dev->tflags |= tf; | ||
281 | else | ||
282 | dev->tflags &= ~tf; | ||
283 | if (dev->tflags && !old_tflags) | ||
284 | mod_timer(&dev->timer, jiffies + ISDN_TIMER_RES); | ||
285 | spin_unlock_irqrestore(&dev->timerlock, flags); | ||
286 | } | ||
287 | |||
288 | /* | ||
289 | * Receive a packet from B-Channel. (Called from low-level-module) | ||
290 | */ | ||
291 | static void | ||
292 | isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb) | ||
293 | { | ||
294 | int i; | ||
295 | |||
296 | if ((i = isdn_dc2minor(di, channel)) == -1) { | ||
297 | dev_kfree_skb(skb); | ||
298 | return; | ||
299 | } | ||
300 | /* Update statistics */ | ||
301 | dev->ibytes[i] += skb->len; | ||
302 | |||
303 | /* First, try to deliver data to network-device */ | ||
304 | if (isdn_net_rcv_skb(i, skb)) | ||
305 | return; | ||
306 | |||
307 | /* V.110 handling | ||
308 | * makes sense for async streams only, so it is | ||
309 | * called after possible net-device delivery. | ||
310 | */ | ||
311 | if (dev->v110[i]) { | ||
312 | atomic_inc(&dev->v110use[i]); | ||
313 | skb = isdn_v110_decode(dev->v110[i], skb); | ||
314 | atomic_dec(&dev->v110use[i]); | ||
315 | if (!skb) | ||
316 | return; | ||
317 | } | ||
318 | |||
319 | /* No network-device found, deliver to tty or raw-channel */ | ||
320 | if (skb->len) { | ||
321 | if (isdn_tty_rcv_skb(i, di, channel, skb)) | ||
322 | return; | ||
323 | wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]); | ||
324 | } else | ||
325 | dev_kfree_skb(skb); | ||
326 | } | ||
327 | |||
328 | /* | ||
329 | * Intercept command from Linklevel to Lowlevel. | ||
330 | * If layer 2 protocol is V.110 and this is not supported by current | ||
331 | * lowlevel-driver, use driver's transparent mode and handle V.110 in | ||
332 | * linklevel instead. | ||
333 | */ | ||
334 | int | ||
335 | isdn_command(isdn_ctrl *cmd) | ||
336 | { | ||
337 | if (cmd->driver == -1) { | ||
338 | printk(KERN_WARNING "isdn_command command(%x) driver -1\n", cmd->command); | ||
339 | return (1); | ||
340 | } | ||
341 | if (!dev->drv[cmd->driver]) { | ||
342 | printk(KERN_WARNING "isdn_command command(%x) dev->drv[%d] NULL\n", | ||
343 | cmd->command, cmd->driver); | ||
344 | return (1); | ||
345 | } | ||
346 | if (!dev->drv[cmd->driver]->interface) { | ||
347 | printk(KERN_WARNING "isdn_command command(%x) dev->drv[%d]->interface NULL\n", | ||
348 | cmd->command, cmd->driver); | ||
349 | return (1); | ||
350 | } | ||
351 | if (cmd->command == ISDN_CMD_SETL2) { | ||
352 | int idx = isdn_dc2minor(cmd->driver, cmd->arg & 255); | ||
353 | unsigned long l2prot = (cmd->arg >> 8) & 255; | ||
354 | unsigned long features = (dev->drv[cmd->driver]->interface->features | ||
355 | >> ISDN_FEATURE_L2_SHIFT) & | ||
356 | ISDN_FEATURE_L2_MASK; | ||
357 | unsigned long l2_feature = (1 << l2prot); | ||
358 | |||
359 | switch (l2prot) { | ||
360 | case ISDN_PROTO_L2_V11096: | ||
361 | case ISDN_PROTO_L2_V11019: | ||
362 | case ISDN_PROTO_L2_V11038: | ||
363 | /* If V.110 requested, but not supported by | ||
364 | * HL-driver, set emulator-flag and change | ||
365 | * Layer-2 to transparent | ||
366 | */ | ||
367 | if (!(features & l2_feature)) { | ||
368 | dev->v110emu[idx] = l2prot; | ||
369 | cmd->arg = (cmd->arg & 255) | | ||
370 | (ISDN_PROTO_L2_TRANS << 8); | ||
371 | } else | ||
372 | dev->v110emu[idx] = 0; | ||
373 | } | ||
374 | } | ||
375 | return dev->drv[cmd->driver]->interface->command(cmd); | ||
376 | } | ||
377 | |||
378 | void | ||
379 | isdn_all_eaz(int di, int ch) | ||
380 | { | ||
381 | isdn_ctrl cmd; | ||
382 | |||
383 | if (di < 0) | ||
384 | return; | ||
385 | cmd.driver = di; | ||
386 | cmd.arg = ch; | ||
387 | cmd.command = ISDN_CMD_SETEAZ; | ||
388 | cmd.parm.num[0] = '\0'; | ||
389 | isdn_command(&cmd); | ||
390 | } | ||
391 | |||
392 | /* | ||
393 | * Begin of a CAPI like LL<->HL interface, currently used only for | ||
394 | * supplementary service (CAPI 2.0 part III) | ||
395 | */ | ||
396 | #include <linux/isdn/capicmd.h> | ||
397 | |||
398 | static int | ||
399 | isdn_capi_rec_hl_msg(capi_msg *cm) | ||
400 | { | ||
401 | switch (cm->Command) { | ||
402 | case CAPI_FACILITY: | ||
403 | /* in the moment only handled in tty */ | ||
404 | return (isdn_tty_capi_facility(cm)); | ||
405 | default: | ||
406 | return (-1); | ||
407 | } | ||
408 | } | ||
409 | |||
410 | static int | ||
411 | isdn_status_callback(isdn_ctrl *c) | ||
412 | { | ||
413 | int di; | ||
414 | u_long flags; | ||
415 | int i; | ||
416 | int r; | ||
417 | int retval = 0; | ||
418 | isdn_ctrl cmd; | ||
419 | isdn_net_dev *p; | ||
420 | |||
421 | di = c->driver; | ||
422 | i = isdn_dc2minor(di, c->arg); | ||
423 | switch (c->command) { | ||
424 | case ISDN_STAT_BSENT: | ||
425 | if (i < 0) | ||
426 | return -1; | ||
427 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) | ||
428 | return 0; | ||
429 | if (isdn_net_stat_callback(i, c)) | ||
430 | return 0; | ||
431 | if (isdn_v110_stat_callback(i, c)) | ||
432 | return 0; | ||
433 | if (isdn_tty_stat_callback(i, c)) | ||
434 | return 0; | ||
435 | wake_up_interruptible(&dev->drv[di]->snd_waitq[c->arg]); | ||
436 | break; | ||
437 | case ISDN_STAT_STAVAIL: | ||
438 | dev->drv[di]->stavail += c->arg; | ||
439 | wake_up_interruptible(&dev->drv[di]->st_waitq); | ||
440 | break; | ||
441 | case ISDN_STAT_RUN: | ||
442 | dev->drv[di]->flags |= DRV_FLAG_RUNNING; | ||
443 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
444 | if (dev->drvmap[i] == di) | ||
445 | isdn_all_eaz(di, dev->chanmap[i]); | ||
446 | set_global_features(); | ||
447 | break; | ||
448 | case ISDN_STAT_STOP: | ||
449 | dev->drv[di]->flags &= ~DRV_FLAG_RUNNING; | ||
450 | break; | ||
451 | case ISDN_STAT_ICALL: | ||
452 | if (i < 0) | ||
453 | return -1; | ||
454 | #ifdef ISDN_DEBUG_STATCALLB | ||
455 | printk(KERN_DEBUG "ICALL (net): %d %ld %s\n", di, c->arg, c->parm.num); | ||
456 | #endif | ||
457 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) { | ||
458 | cmd.driver = di; | ||
459 | cmd.arg = c->arg; | ||
460 | cmd.command = ISDN_CMD_HANGUP; | ||
461 | isdn_command(&cmd); | ||
462 | return 0; | ||
463 | } | ||
464 | /* Try to find a network-interface which will accept incoming call */ | ||
465 | r = ((c->command == ISDN_STAT_ICALLW) ? 0 : isdn_net_find_icall(di, c->arg, i, &c->parm.setup)); | ||
466 | switch (r) { | ||
467 | case 0: | ||
468 | /* No network-device replies. | ||
469 | * Try ttyI's. | ||
470 | * These return 0 on no match, 1 on match and | ||
471 | * 3 on eventually match, if CID is longer. | ||
472 | */ | ||
473 | if (c->command == ISDN_STAT_ICALL) | ||
474 | if ((retval = isdn_tty_find_icall(di, c->arg, &c->parm.setup))) return (retval); | ||
475 | #ifdef CONFIG_ISDN_DIVERSION | ||
476 | if (divert_if) | ||
477 | if ((retval = divert_if->stat_callback(c))) | ||
478 | return (retval); /* processed */ | ||
479 | #endif /* CONFIG_ISDN_DIVERSION */ | ||
480 | if ((!retval) && (dev->drv[di]->flags & DRV_FLAG_REJBUS)) { | ||
481 | /* No tty responding */ | ||
482 | cmd.driver = di; | ||
483 | cmd.arg = c->arg; | ||
484 | cmd.command = ISDN_CMD_HANGUP; | ||
485 | isdn_command(&cmd); | ||
486 | retval = 2; | ||
487 | } | ||
488 | break; | ||
489 | case 1: | ||
490 | /* Schedule connection-setup */ | ||
491 | isdn_net_dial(); | ||
492 | cmd.driver = di; | ||
493 | cmd.arg = c->arg; | ||
494 | cmd.command = ISDN_CMD_ACCEPTD; | ||
495 | for (p = dev->netdev; p; p = p->next) | ||
496 | if (p->local->isdn_channel == cmd.arg) | ||
497 | { | ||
498 | strcpy(cmd.parm.setup.eazmsn, p->local->msn); | ||
499 | isdn_command(&cmd); | ||
500 | retval = 1; | ||
501 | break; | ||
502 | } | ||
503 | break; | ||
504 | |||
505 | case 2: /* For calling back, first reject incoming call ... */ | ||
506 | case 3: /* Interface found, but down, reject call actively */ | ||
507 | retval = 2; | ||
508 | printk(KERN_INFO "isdn: Rejecting Call\n"); | ||
509 | cmd.driver = di; | ||
510 | cmd.arg = c->arg; | ||
511 | cmd.command = ISDN_CMD_HANGUP; | ||
512 | isdn_command(&cmd); | ||
513 | if (r == 3) | ||
514 | break; | ||
515 | /* Fall through */ | ||
516 | case 4: | ||
517 | /* ... then start callback. */ | ||
518 | isdn_net_dial(); | ||
519 | break; | ||
520 | case 5: | ||
521 | /* Number would eventually match, if longer */ | ||
522 | retval = 3; | ||
523 | break; | ||
524 | } | ||
525 | #ifdef ISDN_DEBUG_STATCALLB | ||
526 | printk(KERN_DEBUG "ICALL: ret=%d\n", retval); | ||
527 | #endif | ||
528 | return retval; | ||
529 | break; | ||
530 | case ISDN_STAT_CINF: | ||
531 | if (i < 0) | ||
532 | return -1; | ||
533 | #ifdef ISDN_DEBUG_STATCALLB | ||
534 | printk(KERN_DEBUG "CINF: %ld %s\n", c->arg, c->parm.num); | ||
535 | #endif | ||
536 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) | ||
537 | return 0; | ||
538 | if (strcmp(c->parm.num, "0")) | ||
539 | isdn_net_stat_callback(i, c); | ||
540 | isdn_tty_stat_callback(i, c); | ||
541 | break; | ||
542 | case ISDN_STAT_CAUSE: | ||
543 | #ifdef ISDN_DEBUG_STATCALLB | ||
544 | printk(KERN_DEBUG "CAUSE: %ld %s\n", c->arg, c->parm.num); | ||
545 | #endif | ||
546 | printk(KERN_INFO "isdn: %s,ch%ld cause: %s\n", | ||
547 | dev->drvid[di], c->arg, c->parm.num); | ||
548 | isdn_tty_stat_callback(i, c); | ||
549 | #ifdef CONFIG_ISDN_DIVERSION | ||
550 | if (divert_if) | ||
551 | divert_if->stat_callback(c); | ||
552 | #endif /* CONFIG_ISDN_DIVERSION */ | ||
553 | break; | ||
554 | case ISDN_STAT_DISPLAY: | ||
555 | #ifdef ISDN_DEBUG_STATCALLB | ||
556 | printk(KERN_DEBUG "DISPLAY: %ld %s\n", c->arg, c->parm.display); | ||
557 | #endif | ||
558 | isdn_tty_stat_callback(i, c); | ||
559 | #ifdef CONFIG_ISDN_DIVERSION | ||
560 | if (divert_if) | ||
561 | divert_if->stat_callback(c); | ||
562 | #endif /* CONFIG_ISDN_DIVERSION */ | ||
563 | break; | ||
564 | case ISDN_STAT_DCONN: | ||
565 | if (i < 0) | ||
566 | return -1; | ||
567 | #ifdef ISDN_DEBUG_STATCALLB | ||
568 | printk(KERN_DEBUG "DCONN: %ld\n", c->arg); | ||
569 | #endif | ||
570 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) | ||
571 | return 0; | ||
572 | /* Find any net-device, waiting for D-channel setup */ | ||
573 | if (isdn_net_stat_callback(i, c)) | ||
574 | break; | ||
575 | isdn_v110_stat_callback(i, c); | ||
576 | /* Find any ttyI, waiting for D-channel setup */ | ||
577 | if (isdn_tty_stat_callback(i, c)) { | ||
578 | cmd.driver = di; | ||
579 | cmd.arg = c->arg; | ||
580 | cmd.command = ISDN_CMD_ACCEPTB; | ||
581 | isdn_command(&cmd); | ||
582 | break; | ||
583 | } | ||
584 | break; | ||
585 | case ISDN_STAT_DHUP: | ||
586 | if (i < 0) | ||
587 | return -1; | ||
588 | #ifdef ISDN_DEBUG_STATCALLB | ||
589 | printk(KERN_DEBUG "DHUP: %ld\n", c->arg); | ||
590 | #endif | ||
591 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) | ||
592 | return 0; | ||
593 | dev->drv[di]->online &= ~(1 << (c->arg)); | ||
594 | isdn_info_update(); | ||
595 | /* Signal hangup to network-devices */ | ||
596 | if (isdn_net_stat_callback(i, c)) | ||
597 | break; | ||
598 | isdn_v110_stat_callback(i, c); | ||
599 | if (isdn_tty_stat_callback(i, c)) | ||
600 | break; | ||
601 | #ifdef CONFIG_ISDN_DIVERSION | ||
602 | if (divert_if) | ||
603 | divert_if->stat_callback(c); | ||
604 | #endif /* CONFIG_ISDN_DIVERSION */ | ||
605 | break; | ||
606 | break; | ||
607 | case ISDN_STAT_BCONN: | ||
608 | if (i < 0) | ||
609 | return -1; | ||
610 | #ifdef ISDN_DEBUG_STATCALLB | ||
611 | printk(KERN_DEBUG "BCONN: %ld\n", c->arg); | ||
612 | #endif | ||
613 | /* Signal B-channel-connect to network-devices */ | ||
614 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) | ||
615 | return 0; | ||
616 | dev->drv[di]->online |= (1 << (c->arg)); | ||
617 | isdn_info_update(); | ||
618 | if (isdn_net_stat_callback(i, c)) | ||
619 | break; | ||
620 | isdn_v110_stat_callback(i, c); | ||
621 | if (isdn_tty_stat_callback(i, c)) | ||
622 | break; | ||
623 | break; | ||
624 | case ISDN_STAT_BHUP: | ||
625 | if (i < 0) | ||
626 | return -1; | ||
627 | #ifdef ISDN_DEBUG_STATCALLB | ||
628 | printk(KERN_DEBUG "BHUP: %ld\n", c->arg); | ||
629 | #endif | ||
630 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) | ||
631 | return 0; | ||
632 | dev->drv[di]->online &= ~(1 << (c->arg)); | ||
633 | isdn_info_update(); | ||
634 | #ifdef CONFIG_ISDN_X25 | ||
635 | /* Signal hangup to network-devices */ | ||
636 | if (isdn_net_stat_callback(i, c)) | ||
637 | break; | ||
638 | #endif | ||
639 | isdn_v110_stat_callback(i, c); | ||
640 | if (isdn_tty_stat_callback(i, c)) | ||
641 | break; | ||
642 | break; | ||
643 | case ISDN_STAT_NODCH: | ||
644 | if (i < 0) | ||
645 | return -1; | ||
646 | #ifdef ISDN_DEBUG_STATCALLB | ||
647 | printk(KERN_DEBUG "NODCH: %ld\n", c->arg); | ||
648 | #endif | ||
649 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) | ||
650 | return 0; | ||
651 | if (isdn_net_stat_callback(i, c)) | ||
652 | break; | ||
653 | if (isdn_tty_stat_callback(i, c)) | ||
654 | break; | ||
655 | break; | ||
656 | case ISDN_STAT_ADDCH: | ||
657 | spin_lock_irqsave(&dev->lock, flags); | ||
658 | if (isdn_add_channels(dev->drv[di], di, c->arg, 1)) { | ||
659 | spin_unlock_irqrestore(&dev->lock, flags); | ||
660 | return -1; | ||
661 | } | ||
662 | spin_unlock_irqrestore(&dev->lock, flags); | ||
663 | isdn_info_update(); | ||
664 | break; | ||
665 | case ISDN_STAT_DISCH: | ||
666 | spin_lock_irqsave(&dev->lock, flags); | ||
667 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
668 | if ((dev->drvmap[i] == di) && | ||
669 | (dev->chanmap[i] == c->arg)) { | ||
670 | if (c->parm.num[0]) | ||
671 | dev->usage[i] &= ~ISDN_USAGE_DISABLED; | ||
672 | else | ||
673 | if (USG_NONE(dev->usage[i])) { | ||
674 | dev->usage[i] |= ISDN_USAGE_DISABLED; | ||
675 | } | ||
676 | else | ||
677 | retval = -1; | ||
678 | break; | ||
679 | } | ||
680 | spin_unlock_irqrestore(&dev->lock, flags); | ||
681 | isdn_info_update(); | ||
682 | break; | ||
683 | case ISDN_STAT_UNLOAD: | ||
684 | while (dev->drv[di]->locks > 0) { | ||
685 | isdn_unlock_driver(dev->drv[di]); | ||
686 | } | ||
687 | spin_lock_irqsave(&dev->lock, flags); | ||
688 | isdn_tty_stat_callback(i, c); | ||
689 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
690 | if (dev->drvmap[i] == di) { | ||
691 | dev->drvmap[i] = -1; | ||
692 | dev->chanmap[i] = -1; | ||
693 | dev->usage[i] &= ~ISDN_USAGE_DISABLED; | ||
694 | } | ||
695 | dev->drivers--; | ||
696 | dev->channels -= dev->drv[di]->channels; | ||
697 | kfree(dev->drv[di]->rcverr); | ||
698 | kfree(dev->drv[di]->rcvcount); | ||
699 | for (i = 0; i < dev->drv[di]->channels; i++) | ||
700 | skb_queue_purge(&dev->drv[di]->rpqueue[i]); | ||
701 | kfree(dev->drv[di]->rpqueue); | ||
702 | kfree(dev->drv[di]->rcv_waitq); | ||
703 | kfree(dev->drv[di]); | ||
704 | dev->drv[di] = NULL; | ||
705 | dev->drvid[di][0] = '\0'; | ||
706 | isdn_info_update(); | ||
707 | set_global_features(); | ||
708 | spin_unlock_irqrestore(&dev->lock, flags); | ||
709 | return 0; | ||
710 | case ISDN_STAT_L1ERR: | ||
711 | break; | ||
712 | case CAPI_PUT_MESSAGE: | ||
713 | return (isdn_capi_rec_hl_msg(&c->parm.cmsg)); | ||
714 | #ifdef CONFIG_ISDN_TTY_FAX | ||
715 | case ISDN_STAT_FAXIND: | ||
716 | isdn_tty_stat_callback(i, c); | ||
717 | break; | ||
718 | #endif | ||
719 | #ifdef CONFIG_ISDN_AUDIO | ||
720 | case ISDN_STAT_AUDIO: | ||
721 | isdn_tty_stat_callback(i, c); | ||
722 | break; | ||
723 | #endif | ||
724 | #ifdef CONFIG_ISDN_DIVERSION | ||
725 | case ISDN_STAT_PROT: | ||
726 | case ISDN_STAT_REDIR: | ||
727 | if (divert_if) | ||
728 | return (divert_if->stat_callback(c)); | ||
729 | #endif /* CONFIG_ISDN_DIVERSION */ | ||
730 | /* fall through */ | ||
731 | default: | ||
732 | return -1; | ||
733 | } | ||
734 | return 0; | ||
735 | } | ||
736 | |||
737 | /* | ||
738 | * Get integer from char-pointer, set pointer to end of number | ||
739 | */ | ||
740 | int | ||
741 | isdn_getnum(char **p) | ||
742 | { | ||
743 | int v = -1; | ||
744 | |||
745 | while (*p[0] >= '0' && *p[0] <= '9') | ||
746 | v = ((v < 0) ? 0 : (v * 10)) + (int) ((*p[0]++) - '0'); | ||
747 | return v; | ||
748 | } | ||
749 | |||
750 | #define DLE 0x10 | ||
751 | |||
752 | /* | ||
753 | * isdn_readbchan() tries to get data from the read-queue. | ||
754 | * It MUST be called with interrupts off. | ||
755 | * | ||
756 | * Be aware that this is not an atomic operation when sleep != 0, even though | ||
757 | * interrupts are turned off! Well, like that we are currently only called | ||
758 | * on behalf of a read system call on raw device files (which are documented | ||
759 | * to be dangerous and for debugging purpose only). The inode semaphore | ||
760 | * takes care that this is not called for the same minor device number while | ||
761 | * we are sleeping, but access is not serialized against simultaneous read() | ||
762 | * from the corresponding ttyI device. Can other ugly events, like changes | ||
763 | * of the mapping (di,ch)<->minor, happen during the sleep? --he | ||
764 | */ | ||
765 | int | ||
766 | isdn_readbchan(int di, int channel, u_char *buf, u_char *fp, int len, wait_queue_head_t *sleep) | ||
767 | { | ||
768 | int count; | ||
769 | int count_pull; | ||
770 | int count_put; | ||
771 | int dflag; | ||
772 | struct sk_buff *skb; | ||
773 | u_char *cp; | ||
774 | |||
775 | if (!dev->drv[di]) | ||
776 | return 0; | ||
777 | if (skb_queue_empty(&dev->drv[di]->rpqueue[channel])) { | ||
778 | if (sleep) | ||
779 | wait_event_interruptible(*sleep, | ||
780 | !skb_queue_empty(&dev->drv[di]->rpqueue[channel])); | ||
781 | else | ||
782 | return 0; | ||
783 | } | ||
784 | if (len > dev->drv[di]->rcvcount[channel]) | ||
785 | len = dev->drv[di]->rcvcount[channel]; | ||
786 | cp = buf; | ||
787 | count = 0; | ||
788 | while (len) { | ||
789 | if (!(skb = skb_peek(&dev->drv[di]->rpqueue[channel]))) | ||
790 | break; | ||
791 | #ifdef CONFIG_ISDN_AUDIO | ||
792 | if (ISDN_AUDIO_SKB_LOCK(skb)) | ||
793 | break; | ||
794 | ISDN_AUDIO_SKB_LOCK(skb) = 1; | ||
795 | if ((ISDN_AUDIO_SKB_DLECOUNT(skb)) || (dev->drv[di]->DLEflag & (1 << channel))) { | ||
796 | char *p = skb->data; | ||
797 | unsigned long DLEmask = (1 << channel); | ||
798 | |||
799 | dflag = 0; | ||
800 | count_pull = count_put = 0; | ||
801 | while ((count_pull < skb->len) && (len > 0)) { | ||
802 | len--; | ||
803 | if (dev->drv[di]->DLEflag & DLEmask) { | ||
804 | *cp++ = DLE; | ||
805 | dev->drv[di]->DLEflag &= ~DLEmask; | ||
806 | } else { | ||
807 | *cp++ = *p; | ||
808 | if (*p == DLE) { | ||
809 | dev->drv[di]->DLEflag |= DLEmask; | ||
810 | (ISDN_AUDIO_SKB_DLECOUNT(skb))--; | ||
811 | } | ||
812 | p++; | ||
813 | count_pull++; | ||
814 | } | ||
815 | count_put++; | ||
816 | } | ||
817 | if (count_pull >= skb->len) | ||
818 | dflag = 1; | ||
819 | } else { | ||
820 | #endif | ||
821 | /* No DLE's in buff, so simply copy it */ | ||
822 | dflag = 1; | ||
823 | if ((count_pull = skb->len) > len) { | ||
824 | count_pull = len; | ||
825 | dflag = 0; | ||
826 | } | ||
827 | count_put = count_pull; | ||
828 | skb_copy_from_linear_data(skb, cp, count_put); | ||
829 | cp += count_put; | ||
830 | len -= count_put; | ||
831 | #ifdef CONFIG_ISDN_AUDIO | ||
832 | } | ||
833 | #endif | ||
834 | count += count_put; | ||
835 | if (fp) { | ||
836 | memset(fp, 0, count_put); | ||
837 | fp += count_put; | ||
838 | } | ||
839 | if (dflag) { | ||
840 | /* We got all the data in this buff. | ||
841 | * Now we can dequeue it. | ||
842 | */ | ||
843 | if (fp) | ||
844 | *(fp - 1) = 0xff; | ||
845 | #ifdef CONFIG_ISDN_AUDIO | ||
846 | ISDN_AUDIO_SKB_LOCK(skb) = 0; | ||
847 | #endif | ||
848 | skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]); | ||
849 | dev_kfree_skb(skb); | ||
850 | } else { | ||
851 | /* Not yet emptied this buff, so it | ||
852 | * must stay in the queue, for further calls | ||
853 | * but we pull off the data we got until now. | ||
854 | */ | ||
855 | skb_pull(skb, count_pull); | ||
856 | #ifdef CONFIG_ISDN_AUDIO | ||
857 | ISDN_AUDIO_SKB_LOCK(skb) = 0; | ||
858 | #endif | ||
859 | } | ||
860 | dev->drv[di]->rcvcount[channel] -= count_put; | ||
861 | } | ||
862 | return count; | ||
863 | } | ||
864 | |||
865 | /* | ||
866 | * isdn_readbchan_tty() tries to get data from the read-queue. | ||
867 | * It MUST be called with interrupts off. | ||
868 | * | ||
869 | * Be aware that this is not an atomic operation when sleep != 0, even though | ||
870 | * interrupts are turned off! Well, like that we are currently only called | ||
871 | * on behalf of a read system call on raw device files (which are documented | ||
872 | * to be dangerous and for debugging purpose only). The inode semaphore | ||
873 | * takes care that this is not called for the same minor device number while | ||
874 | * we are sleeping, but access is not serialized against simultaneous read() | ||
875 | * from the corresponding ttyI device. Can other ugly events, like changes | ||
876 | * of the mapping (di,ch)<->minor, happen during the sleep? --he | ||
877 | */ | ||
878 | int | ||
879 | isdn_readbchan_tty(int di, int channel, struct tty_port *port, int cisco_hack) | ||
880 | { | ||
881 | int count; | ||
882 | int count_pull; | ||
883 | int count_put; | ||
884 | int dflag; | ||
885 | struct sk_buff *skb; | ||
886 | char last = 0; | ||
887 | int len; | ||
888 | |||
889 | if (!dev->drv[di]) | ||
890 | return 0; | ||
891 | if (skb_queue_empty(&dev->drv[di]->rpqueue[channel])) | ||
892 | return 0; | ||
893 | |||
894 | len = tty_buffer_request_room(port, dev->drv[di]->rcvcount[channel]); | ||
895 | if (len == 0) | ||
896 | return len; | ||
897 | |||
898 | count = 0; | ||
899 | while (len) { | ||
900 | if (!(skb = skb_peek(&dev->drv[di]->rpqueue[channel]))) | ||
901 | break; | ||
902 | #ifdef CONFIG_ISDN_AUDIO | ||
903 | if (ISDN_AUDIO_SKB_LOCK(skb)) | ||
904 | break; | ||
905 | ISDN_AUDIO_SKB_LOCK(skb) = 1; | ||
906 | if ((ISDN_AUDIO_SKB_DLECOUNT(skb)) || (dev->drv[di]->DLEflag & (1 << channel))) { | ||
907 | char *p = skb->data; | ||
908 | unsigned long DLEmask = (1 << channel); | ||
909 | |||
910 | dflag = 0; | ||
911 | count_pull = count_put = 0; | ||
912 | while ((count_pull < skb->len) && (len > 0)) { | ||
913 | /* push every character but the last to the tty buffer directly */ | ||
914 | if (count_put) | ||
915 | tty_insert_flip_char(port, last, TTY_NORMAL); | ||
916 | len--; | ||
917 | if (dev->drv[di]->DLEflag & DLEmask) { | ||
918 | last = DLE; | ||
919 | dev->drv[di]->DLEflag &= ~DLEmask; | ||
920 | } else { | ||
921 | last = *p; | ||
922 | if (last == DLE) { | ||
923 | dev->drv[di]->DLEflag |= DLEmask; | ||
924 | (ISDN_AUDIO_SKB_DLECOUNT(skb))--; | ||
925 | } | ||
926 | p++; | ||
927 | count_pull++; | ||
928 | } | ||
929 | count_put++; | ||
930 | } | ||
931 | if (count_pull >= skb->len) | ||
932 | dflag = 1; | ||
933 | } else { | ||
934 | #endif | ||
935 | /* No DLE's in buff, so simply copy it */ | ||
936 | dflag = 1; | ||
937 | if ((count_pull = skb->len) > len) { | ||
938 | count_pull = len; | ||
939 | dflag = 0; | ||
940 | } | ||
941 | count_put = count_pull; | ||
942 | if (count_put > 1) | ||
943 | tty_insert_flip_string(port, skb->data, count_put - 1); | ||
944 | last = skb->data[count_put - 1]; | ||
945 | len -= count_put; | ||
946 | #ifdef CONFIG_ISDN_AUDIO | ||
947 | } | ||
948 | #endif | ||
949 | count += count_put; | ||
950 | if (dflag) { | ||
951 | /* We got all the data in this buff. | ||
952 | * Now we can dequeue it. | ||
953 | */ | ||
954 | if (cisco_hack) | ||
955 | tty_insert_flip_char(port, last, 0xFF); | ||
956 | else | ||
957 | tty_insert_flip_char(port, last, TTY_NORMAL); | ||
958 | #ifdef CONFIG_ISDN_AUDIO | ||
959 | ISDN_AUDIO_SKB_LOCK(skb) = 0; | ||
960 | #endif | ||
961 | skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]); | ||
962 | dev_kfree_skb(skb); | ||
963 | } else { | ||
964 | tty_insert_flip_char(port, last, TTY_NORMAL); | ||
965 | /* Not yet emptied this buff, so it | ||
966 | * must stay in the queue, for further calls | ||
967 | * but we pull off the data we got until now. | ||
968 | */ | ||
969 | skb_pull(skb, count_pull); | ||
970 | #ifdef CONFIG_ISDN_AUDIO | ||
971 | ISDN_AUDIO_SKB_LOCK(skb) = 0; | ||
972 | #endif | ||
973 | } | ||
974 | dev->drv[di]->rcvcount[channel] -= count_put; | ||
975 | } | ||
976 | return count; | ||
977 | } | ||
978 | |||
979 | |||
980 | static inline int | ||
981 | isdn_minor2drv(int minor) | ||
982 | { | ||
983 | return (dev->drvmap[minor]); | ||
984 | } | ||
985 | |||
986 | static inline int | ||
987 | isdn_minor2chan(int minor) | ||
988 | { | ||
989 | return (dev->chanmap[minor]); | ||
990 | } | ||
991 | |||
992 | static char * | ||
993 | isdn_statstr(void) | ||
994 | { | ||
995 | static char istatbuf[2048]; | ||
996 | char *p; | ||
997 | int i; | ||
998 | |||
999 | sprintf(istatbuf, "idmap:\t"); | ||
1000 | p = istatbuf + strlen(istatbuf); | ||
1001 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
1002 | sprintf(p, "%s ", (dev->drvmap[i] < 0) ? "-" : dev->drvid[dev->drvmap[i]]); | ||
1003 | p = istatbuf + strlen(istatbuf); | ||
1004 | } | ||
1005 | sprintf(p, "\nchmap:\t"); | ||
1006 | p = istatbuf + strlen(istatbuf); | ||
1007 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
1008 | sprintf(p, "%d ", dev->chanmap[i]); | ||
1009 | p = istatbuf + strlen(istatbuf); | ||
1010 | } | ||
1011 | sprintf(p, "\ndrmap:\t"); | ||
1012 | p = istatbuf + strlen(istatbuf); | ||
1013 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
1014 | sprintf(p, "%d ", dev->drvmap[i]); | ||
1015 | p = istatbuf + strlen(istatbuf); | ||
1016 | } | ||
1017 | sprintf(p, "\nusage:\t"); | ||
1018 | p = istatbuf + strlen(istatbuf); | ||
1019 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
1020 | sprintf(p, "%d ", dev->usage[i]); | ||
1021 | p = istatbuf + strlen(istatbuf); | ||
1022 | } | ||
1023 | sprintf(p, "\nflags:\t"); | ||
1024 | p = istatbuf + strlen(istatbuf); | ||
1025 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) { | ||
1026 | if (dev->drv[i]) { | ||
1027 | sprintf(p, "%ld ", dev->drv[i]->online); | ||
1028 | p = istatbuf + strlen(istatbuf); | ||
1029 | } else { | ||
1030 | sprintf(p, "? "); | ||
1031 | p = istatbuf + strlen(istatbuf); | ||
1032 | } | ||
1033 | } | ||
1034 | sprintf(p, "\nphone:\t"); | ||
1035 | p = istatbuf + strlen(istatbuf); | ||
1036 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
1037 | sprintf(p, "%s ", dev->num[i]); | ||
1038 | p = istatbuf + strlen(istatbuf); | ||
1039 | } | ||
1040 | sprintf(p, "\n"); | ||
1041 | return istatbuf; | ||
1042 | } | ||
1043 | |||
1044 | /* Module interface-code */ | ||
1045 | |||
1046 | void | ||
1047 | isdn_info_update(void) | ||
1048 | { | ||
1049 | infostruct *p = dev->infochain; | ||
1050 | |||
1051 | while (p) { | ||
1052 | *(p->private) = 1; | ||
1053 | p = (infostruct *) p->next; | ||
1054 | } | ||
1055 | wake_up_interruptible(&(dev->info_waitq)); | ||
1056 | } | ||
1057 | |||
1058 | static ssize_t | ||
1059 | isdn_read(struct file *file, char __user *buf, size_t count, loff_t *off) | ||
1060 | { | ||
1061 | uint minor = iminor(file_inode(file)); | ||
1062 | int len = 0; | ||
1063 | int drvidx; | ||
1064 | int chidx; | ||
1065 | int retval; | ||
1066 | char *p; | ||
1067 | |||
1068 | mutex_lock(&isdn_mutex); | ||
1069 | if (minor == ISDN_MINOR_STATUS) { | ||
1070 | if (!file->private_data) { | ||
1071 | if (file->f_flags & O_NONBLOCK) { | ||
1072 | retval = -EAGAIN; | ||
1073 | goto out; | ||
1074 | } | ||
1075 | wait_event_interruptible(dev->info_waitq, | ||
1076 | file->private_data); | ||
1077 | } | ||
1078 | p = isdn_statstr(); | ||
1079 | file->private_data = NULL; | ||
1080 | if ((len = strlen(p)) <= count) { | ||
1081 | if (copy_to_user(buf, p, len)) { | ||
1082 | retval = -EFAULT; | ||
1083 | goto out; | ||
1084 | } | ||
1085 | *off += len; | ||
1086 | retval = len; | ||
1087 | goto out; | ||
1088 | } | ||
1089 | retval = 0; | ||
1090 | goto out; | ||
1091 | } | ||
1092 | if (!dev->drivers) { | ||
1093 | retval = -ENODEV; | ||
1094 | goto out; | ||
1095 | } | ||
1096 | if (minor <= ISDN_MINOR_BMAX) { | ||
1097 | printk(KERN_WARNING "isdn_read minor %d obsolete!\n", minor); | ||
1098 | drvidx = isdn_minor2drv(minor); | ||
1099 | if (drvidx < 0) { | ||
1100 | retval = -ENODEV; | ||
1101 | goto out; | ||
1102 | } | ||
1103 | if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) { | ||
1104 | retval = -ENODEV; | ||
1105 | goto out; | ||
1106 | } | ||
1107 | chidx = isdn_minor2chan(minor); | ||
1108 | if (!(p = kmalloc(count, GFP_KERNEL))) { | ||
1109 | retval = -ENOMEM; | ||
1110 | goto out; | ||
1111 | } | ||
1112 | len = isdn_readbchan(drvidx, chidx, p, NULL, count, | ||
1113 | &dev->drv[drvidx]->rcv_waitq[chidx]); | ||
1114 | *off += len; | ||
1115 | if (copy_to_user(buf, p, len)) | ||
1116 | len = -EFAULT; | ||
1117 | kfree(p); | ||
1118 | retval = len; | ||
1119 | goto out; | ||
1120 | } | ||
1121 | if (minor <= ISDN_MINOR_CTRLMAX) { | ||
1122 | drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); | ||
1123 | if (drvidx < 0) { | ||
1124 | retval = -ENODEV; | ||
1125 | goto out; | ||
1126 | } | ||
1127 | if (!dev->drv[drvidx]->stavail) { | ||
1128 | if (file->f_flags & O_NONBLOCK) { | ||
1129 | retval = -EAGAIN; | ||
1130 | goto out; | ||
1131 | } | ||
1132 | wait_event_interruptible(dev->drv[drvidx]->st_waitq, | ||
1133 | dev->drv[drvidx]->stavail); | ||
1134 | } | ||
1135 | if (dev->drv[drvidx]->interface->readstat) { | ||
1136 | if (count > dev->drv[drvidx]->stavail) | ||
1137 | count = dev->drv[drvidx]->stavail; | ||
1138 | len = dev->drv[drvidx]->interface->readstat(buf, count, | ||
1139 | drvidx, isdn_minor2chan(minor - ISDN_MINOR_CTRL)); | ||
1140 | if (len < 0) { | ||
1141 | retval = len; | ||
1142 | goto out; | ||
1143 | } | ||
1144 | } else { | ||
1145 | len = 0; | ||
1146 | } | ||
1147 | if (len) | ||
1148 | dev->drv[drvidx]->stavail -= len; | ||
1149 | else | ||
1150 | dev->drv[drvidx]->stavail = 0; | ||
1151 | *off += len; | ||
1152 | retval = len; | ||
1153 | goto out; | ||
1154 | } | ||
1155 | #ifdef CONFIG_ISDN_PPP | ||
1156 | if (minor <= ISDN_MINOR_PPPMAX) { | ||
1157 | retval = isdn_ppp_read(minor - ISDN_MINOR_PPP, file, buf, count); | ||
1158 | goto out; | ||
1159 | } | ||
1160 | #endif | ||
1161 | retval = -ENODEV; | ||
1162 | out: | ||
1163 | mutex_unlock(&isdn_mutex); | ||
1164 | return retval; | ||
1165 | } | ||
1166 | |||
1167 | static ssize_t | ||
1168 | isdn_write(struct file *file, const char __user *buf, size_t count, loff_t *off) | ||
1169 | { | ||
1170 | uint minor = iminor(file_inode(file)); | ||
1171 | int drvidx; | ||
1172 | int chidx; | ||
1173 | int retval; | ||
1174 | |||
1175 | if (minor == ISDN_MINOR_STATUS) | ||
1176 | return -EPERM; | ||
1177 | if (!dev->drivers) | ||
1178 | return -ENODEV; | ||
1179 | |||
1180 | mutex_lock(&isdn_mutex); | ||
1181 | if (minor <= ISDN_MINOR_BMAX) { | ||
1182 | printk(KERN_WARNING "isdn_write minor %d obsolete!\n", minor); | ||
1183 | drvidx = isdn_minor2drv(minor); | ||
1184 | if (drvidx < 0) { | ||
1185 | retval = -ENODEV; | ||
1186 | goto out; | ||
1187 | } | ||
1188 | if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) { | ||
1189 | retval = -ENODEV; | ||
1190 | goto out; | ||
1191 | } | ||
1192 | chidx = isdn_minor2chan(minor); | ||
1193 | wait_event_interruptible(dev->drv[drvidx]->snd_waitq[chidx], | ||
1194 | (retval = isdn_writebuf_stub(drvidx, chidx, buf, count))); | ||
1195 | goto out; | ||
1196 | } | ||
1197 | if (minor <= ISDN_MINOR_CTRLMAX) { | ||
1198 | drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); | ||
1199 | if (drvidx < 0) { | ||
1200 | retval = -ENODEV; | ||
1201 | goto out; | ||
1202 | } | ||
1203 | /* | ||
1204 | * We want to use the isdnctrl device to load the firmware | ||
1205 | * | ||
1206 | if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) | ||
1207 | return -ENODEV; | ||
1208 | */ | ||
1209 | if (dev->drv[drvidx]->interface->writecmd) | ||
1210 | retval = dev->drv[drvidx]->interface-> | ||
1211 | writecmd(buf, count, drvidx, | ||
1212 | isdn_minor2chan(minor - ISDN_MINOR_CTRL)); | ||
1213 | else | ||
1214 | retval = count; | ||
1215 | goto out; | ||
1216 | } | ||
1217 | #ifdef CONFIG_ISDN_PPP | ||
1218 | if (minor <= ISDN_MINOR_PPPMAX) { | ||
1219 | retval = isdn_ppp_write(minor - ISDN_MINOR_PPP, file, buf, count); | ||
1220 | goto out; | ||
1221 | } | ||
1222 | #endif | ||
1223 | retval = -ENODEV; | ||
1224 | out: | ||
1225 | mutex_unlock(&isdn_mutex); | ||
1226 | return retval; | ||
1227 | } | ||
1228 | |||
1229 | static __poll_t | ||
1230 | isdn_poll(struct file *file, poll_table *wait) | ||
1231 | { | ||
1232 | __poll_t mask = 0; | ||
1233 | unsigned int minor = iminor(file_inode(file)); | ||
1234 | int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); | ||
1235 | |||
1236 | mutex_lock(&isdn_mutex); | ||
1237 | if (minor == ISDN_MINOR_STATUS) { | ||
1238 | poll_wait(file, &(dev->info_waitq), wait); | ||
1239 | /* mask = EPOLLOUT | EPOLLWRNORM; */ | ||
1240 | if (file->private_data) { | ||
1241 | mask |= EPOLLIN | EPOLLRDNORM; | ||
1242 | } | ||
1243 | goto out; | ||
1244 | } | ||
1245 | if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) { | ||
1246 | if (drvidx < 0) { | ||
1247 | /* driver deregistered while file open */ | ||
1248 | mask = EPOLLHUP; | ||
1249 | goto out; | ||
1250 | } | ||
1251 | poll_wait(file, &(dev->drv[drvidx]->st_waitq), wait); | ||
1252 | mask = EPOLLOUT | EPOLLWRNORM; | ||
1253 | if (dev->drv[drvidx]->stavail) { | ||
1254 | mask |= EPOLLIN | EPOLLRDNORM; | ||
1255 | } | ||
1256 | goto out; | ||
1257 | } | ||
1258 | #ifdef CONFIG_ISDN_PPP | ||
1259 | if (minor <= ISDN_MINOR_PPPMAX) { | ||
1260 | mask = isdn_ppp_poll(file, wait); | ||
1261 | goto out; | ||
1262 | } | ||
1263 | #endif | ||
1264 | mask = EPOLLERR; | ||
1265 | out: | ||
1266 | mutex_unlock(&isdn_mutex); | ||
1267 | return mask; | ||
1268 | } | ||
1269 | |||
1270 | |||
1271 | static int | ||
1272 | isdn_ioctl(struct file *file, uint cmd, ulong arg) | ||
1273 | { | ||
1274 | uint minor = iminor(file_inode(file)); | ||
1275 | isdn_ctrl c; | ||
1276 | int drvidx; | ||
1277 | int ret; | ||
1278 | int i; | ||
1279 | char __user *p; | ||
1280 | char *s; | ||
1281 | union iocpar { | ||
1282 | char name[10]; | ||
1283 | char bname[22]; | ||
1284 | isdn_ioctl_struct iocts; | ||
1285 | isdn_net_ioctl_phone phone; | ||
1286 | isdn_net_ioctl_cfg cfg; | ||
1287 | } iocpar; | ||
1288 | void __user *argp = (void __user *)arg; | ||
1289 | |||
1290 | #define name iocpar.name | ||
1291 | #define bname iocpar.bname | ||
1292 | #define iocts iocpar.iocts | ||
1293 | #define phone iocpar.phone | ||
1294 | #define cfg iocpar.cfg | ||
1295 | |||
1296 | if (minor == ISDN_MINOR_STATUS) { | ||
1297 | switch (cmd) { | ||
1298 | case IIOCGETDVR: | ||
1299 | return (TTY_DV + | ||
1300 | (NET_DV << 8) + | ||
1301 | (INF_DV << 16)); | ||
1302 | case IIOCGETCPS: | ||
1303 | if (arg) { | ||
1304 | ulong __user *p = argp; | ||
1305 | int i; | ||
1306 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
1307 | put_user(dev->ibytes[i], p++); | ||
1308 | put_user(dev->obytes[i], p++); | ||
1309 | } | ||
1310 | return 0; | ||
1311 | } else | ||
1312 | return -EINVAL; | ||
1313 | break; | ||
1314 | case IIOCNETGPN: | ||
1315 | /* Get peer phone number of a connected | ||
1316 | * isdn network interface */ | ||
1317 | if (arg) { | ||
1318 | if (copy_from_user(&phone, argp, sizeof(phone))) | ||
1319 | return -EFAULT; | ||
1320 | return isdn_net_getpeer(&phone, argp); | ||
1321 | } else | ||
1322 | return -EINVAL; | ||
1323 | default: | ||
1324 | return -EINVAL; | ||
1325 | } | ||
1326 | } | ||
1327 | if (!dev->drivers) | ||
1328 | return -ENODEV; | ||
1329 | if (minor <= ISDN_MINOR_BMAX) { | ||
1330 | drvidx = isdn_minor2drv(minor); | ||
1331 | if (drvidx < 0) | ||
1332 | return -ENODEV; | ||
1333 | if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) | ||
1334 | return -ENODEV; | ||
1335 | return 0; | ||
1336 | } | ||
1337 | if (minor <= ISDN_MINOR_CTRLMAX) { | ||
1338 | /* | ||
1339 | * isdn net devices manage lots of configuration variables as linked lists. | ||
1340 | * Those lists must only be manipulated from user space. Some of the ioctl's | ||
1341 | * service routines access user space and are not atomic. Therefore, ioctl's | ||
1342 | * manipulating the lists and ioctl's sleeping while accessing the lists | ||
1343 | * are serialized by means of a semaphore. | ||
1344 | */ | ||
1345 | switch (cmd) { | ||
1346 | case IIOCNETDWRSET: | ||
1347 | printk(KERN_INFO "INFO: ISDN_DW_ABC_EXTENSION not enabled\n"); | ||
1348 | return (-EINVAL); | ||
1349 | case IIOCNETLCR: | ||
1350 | printk(KERN_INFO "INFO: ISDN_ABC_LCR_SUPPORT not enabled\n"); | ||
1351 | return -ENODEV; | ||
1352 | case IIOCNETAIF: | ||
1353 | /* Add a network-interface */ | ||
1354 | if (arg) { | ||
1355 | if (copy_from_user(name, argp, sizeof(name))) | ||
1356 | return -EFAULT; | ||
1357 | s = name; | ||
1358 | } else { | ||
1359 | s = NULL; | ||
1360 | } | ||
1361 | ret = mutex_lock_interruptible(&dev->mtx); | ||
1362 | if (ret) return ret; | ||
1363 | if ((s = isdn_net_new(s, NULL))) { | ||
1364 | if (copy_to_user(argp, s, strlen(s) + 1)) { | ||
1365 | ret = -EFAULT; | ||
1366 | } else { | ||
1367 | ret = 0; | ||
1368 | } | ||
1369 | } else | ||
1370 | ret = -ENODEV; | ||
1371 | mutex_unlock(&dev->mtx); | ||
1372 | return ret; | ||
1373 | case IIOCNETASL: | ||
1374 | /* Add a slave to a network-interface */ | ||
1375 | if (arg) { | ||
1376 | if (copy_from_user(bname, argp, sizeof(bname) - 1)) | ||
1377 | return -EFAULT; | ||
1378 | bname[sizeof(bname)-1] = 0; | ||
1379 | } else | ||
1380 | return -EINVAL; | ||
1381 | ret = mutex_lock_interruptible(&dev->mtx); | ||
1382 | if (ret) return ret; | ||
1383 | if ((s = isdn_net_newslave(bname))) { | ||
1384 | if (copy_to_user(argp, s, strlen(s) + 1)) { | ||
1385 | ret = -EFAULT; | ||
1386 | } else { | ||
1387 | ret = 0; | ||
1388 | } | ||
1389 | } else | ||
1390 | ret = -ENODEV; | ||
1391 | mutex_unlock(&dev->mtx); | ||
1392 | return ret; | ||
1393 | case IIOCNETDIF: | ||
1394 | /* Delete a network-interface */ | ||
1395 | if (arg) { | ||
1396 | if (copy_from_user(name, argp, sizeof(name))) | ||
1397 | return -EFAULT; | ||
1398 | ret = mutex_lock_interruptible(&dev->mtx); | ||
1399 | if (ret) return ret; | ||
1400 | ret = isdn_net_rm(name); | ||
1401 | mutex_unlock(&dev->mtx); | ||
1402 | return ret; | ||
1403 | } else | ||
1404 | return -EINVAL; | ||
1405 | case IIOCNETSCF: | ||
1406 | /* Set configurable parameters of a network-interface */ | ||
1407 | if (arg) { | ||
1408 | if (copy_from_user(&cfg, argp, sizeof(cfg))) | ||
1409 | return -EFAULT; | ||
1410 | return isdn_net_setcfg(&cfg); | ||
1411 | } else | ||
1412 | return -EINVAL; | ||
1413 | case IIOCNETGCF: | ||
1414 | /* Get configurable parameters of a network-interface */ | ||
1415 | if (arg) { | ||
1416 | if (copy_from_user(&cfg, argp, sizeof(cfg))) | ||
1417 | return -EFAULT; | ||
1418 | if (!(ret = isdn_net_getcfg(&cfg))) { | ||
1419 | if (copy_to_user(argp, &cfg, sizeof(cfg))) | ||
1420 | return -EFAULT; | ||
1421 | } | ||
1422 | return ret; | ||
1423 | } else | ||
1424 | return -EINVAL; | ||
1425 | case IIOCNETANM: | ||
1426 | /* Add a phone-number to a network-interface */ | ||
1427 | if (arg) { | ||
1428 | if (copy_from_user(&phone, argp, sizeof(phone))) | ||
1429 | return -EFAULT; | ||
1430 | ret = mutex_lock_interruptible(&dev->mtx); | ||
1431 | if (ret) return ret; | ||
1432 | ret = isdn_net_addphone(&phone); | ||
1433 | mutex_unlock(&dev->mtx); | ||
1434 | return ret; | ||
1435 | } else | ||
1436 | return -EINVAL; | ||
1437 | case IIOCNETGNM: | ||
1438 | /* Get list of phone-numbers of a network-interface */ | ||
1439 | if (arg) { | ||
1440 | if (copy_from_user(&phone, argp, sizeof(phone))) | ||
1441 | return -EFAULT; | ||
1442 | ret = mutex_lock_interruptible(&dev->mtx); | ||
1443 | if (ret) return ret; | ||
1444 | ret = isdn_net_getphones(&phone, argp); | ||
1445 | mutex_unlock(&dev->mtx); | ||
1446 | return ret; | ||
1447 | } else | ||
1448 | return -EINVAL; | ||
1449 | case IIOCNETDNM: | ||
1450 | /* Delete a phone-number of a network-interface */ | ||
1451 | if (arg) { | ||
1452 | if (copy_from_user(&phone, argp, sizeof(phone))) | ||
1453 | return -EFAULT; | ||
1454 | ret = mutex_lock_interruptible(&dev->mtx); | ||
1455 | if (ret) return ret; | ||
1456 | ret = isdn_net_delphone(&phone); | ||
1457 | mutex_unlock(&dev->mtx); | ||
1458 | return ret; | ||
1459 | } else | ||
1460 | return -EINVAL; | ||
1461 | case IIOCNETDIL: | ||
1462 | /* Force dialing of a network-interface */ | ||
1463 | if (arg) { | ||
1464 | if (copy_from_user(name, argp, sizeof(name))) | ||
1465 | return -EFAULT; | ||
1466 | return isdn_net_force_dial(name); | ||
1467 | } else | ||
1468 | return -EINVAL; | ||
1469 | #ifdef CONFIG_ISDN_PPP | ||
1470 | case IIOCNETALN: | ||
1471 | if (!arg) | ||
1472 | return -EINVAL; | ||
1473 | if (copy_from_user(name, argp, sizeof(name))) | ||
1474 | return -EFAULT; | ||
1475 | return isdn_ppp_dial_slave(name); | ||
1476 | case IIOCNETDLN: | ||
1477 | if (!arg) | ||
1478 | return -EINVAL; | ||
1479 | if (copy_from_user(name, argp, sizeof(name))) | ||
1480 | return -EFAULT; | ||
1481 | return isdn_ppp_hangup_slave(name); | ||
1482 | #endif | ||
1483 | case IIOCNETHUP: | ||
1484 | /* Force hangup of a network-interface */ | ||
1485 | if (!arg) | ||
1486 | return -EINVAL; | ||
1487 | if (copy_from_user(name, argp, sizeof(name))) | ||
1488 | return -EFAULT; | ||
1489 | return isdn_net_force_hangup(name); | ||
1490 | break; | ||
1491 | case IIOCSETVER: | ||
1492 | dev->net_verbose = arg; | ||
1493 | printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose); | ||
1494 | return 0; | ||
1495 | case IIOCSETGST: | ||
1496 | if (arg) | ||
1497 | dev->global_flags |= ISDN_GLOBAL_STOPPED; | ||
1498 | else | ||
1499 | dev->global_flags &= ~ISDN_GLOBAL_STOPPED; | ||
1500 | printk(KERN_INFO "isdn: Global Mode %s\n", | ||
1501 | (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running"); | ||
1502 | return 0; | ||
1503 | case IIOCSETBRJ: | ||
1504 | drvidx = -1; | ||
1505 | if (arg) { | ||
1506 | int i; | ||
1507 | char *p; | ||
1508 | if (copy_from_user(&iocts, argp, | ||
1509 | sizeof(isdn_ioctl_struct))) | ||
1510 | return -EFAULT; | ||
1511 | iocts.drvid[sizeof(iocts.drvid) - 1] = 0; | ||
1512 | if (strlen(iocts.drvid)) { | ||
1513 | if ((p = strchr(iocts.drvid, ','))) | ||
1514 | *p = 0; | ||
1515 | drvidx = -1; | ||
1516 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) | ||
1517 | if (!(strcmp(dev->drvid[i], iocts.drvid))) { | ||
1518 | drvidx = i; | ||
1519 | break; | ||
1520 | } | ||
1521 | } | ||
1522 | } | ||
1523 | if (drvidx == -1) | ||
1524 | return -ENODEV; | ||
1525 | if (iocts.arg) | ||
1526 | dev->drv[drvidx]->flags |= DRV_FLAG_REJBUS; | ||
1527 | else | ||
1528 | dev->drv[drvidx]->flags &= ~DRV_FLAG_REJBUS; | ||
1529 | return 0; | ||
1530 | case IIOCSIGPRF: | ||
1531 | dev->profd = current; | ||
1532 | return 0; | ||
1533 | break; | ||
1534 | case IIOCGETPRF: | ||
1535 | /* Get all Modem-Profiles */ | ||
1536 | if (arg) { | ||
1537 | char __user *p = argp; | ||
1538 | int i; | ||
1539 | |||
1540 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
1541 | if (copy_to_user(p, dev->mdm.info[i].emu.profile, | ||
1542 | ISDN_MODEM_NUMREG)) | ||
1543 | return -EFAULT; | ||
1544 | p += ISDN_MODEM_NUMREG; | ||
1545 | if (copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN)) | ||
1546 | return -EFAULT; | ||
1547 | p += ISDN_MSNLEN; | ||
1548 | if (copy_to_user(p, dev->mdm.info[i].emu.plmsn, ISDN_LMSNLEN)) | ||
1549 | return -EFAULT; | ||
1550 | p += ISDN_LMSNLEN; | ||
1551 | } | ||
1552 | return (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN) * ISDN_MAX_CHANNELS; | ||
1553 | } else | ||
1554 | return -EINVAL; | ||
1555 | break; | ||
1556 | case IIOCSETPRF: | ||
1557 | /* Set all Modem-Profiles */ | ||
1558 | if (arg) { | ||
1559 | char __user *p = argp; | ||
1560 | int i; | ||
1561 | |||
1562 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
1563 | if (copy_from_user(dev->mdm.info[i].emu.profile, p, | ||
1564 | ISDN_MODEM_NUMREG)) | ||
1565 | return -EFAULT; | ||
1566 | p += ISDN_MODEM_NUMREG; | ||
1567 | if (copy_from_user(dev->mdm.info[i].emu.plmsn, p, ISDN_LMSNLEN)) | ||
1568 | return -EFAULT; | ||
1569 | p += ISDN_LMSNLEN; | ||
1570 | if (copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN)) | ||
1571 | return -EFAULT; | ||
1572 | p += ISDN_MSNLEN; | ||
1573 | } | ||
1574 | return 0; | ||
1575 | } else | ||
1576 | return -EINVAL; | ||
1577 | break; | ||
1578 | case IIOCSETMAP: | ||
1579 | case IIOCGETMAP: | ||
1580 | /* Set/Get MSN->EAZ-Mapping for a driver */ | ||
1581 | if (arg) { | ||
1582 | |||
1583 | if (copy_from_user(&iocts, argp, | ||
1584 | sizeof(isdn_ioctl_struct))) | ||
1585 | return -EFAULT; | ||
1586 | iocts.drvid[sizeof(iocts.drvid) - 1] = 0; | ||
1587 | if (strlen(iocts.drvid)) { | ||
1588 | drvidx = -1; | ||
1589 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) | ||
1590 | if (!(strcmp(dev->drvid[i], iocts.drvid))) { | ||
1591 | drvidx = i; | ||
1592 | break; | ||
1593 | } | ||
1594 | } else | ||
1595 | drvidx = 0; | ||
1596 | if (drvidx == -1) | ||
1597 | return -ENODEV; | ||
1598 | if (cmd == IIOCSETMAP) { | ||
1599 | int loop = 1; | ||
1600 | |||
1601 | p = (char __user *) iocts.arg; | ||
1602 | i = 0; | ||
1603 | while (loop) { | ||
1604 | int j = 0; | ||
1605 | |||
1606 | while (1) { | ||
1607 | get_user(bname[j], p++); | ||
1608 | switch (bname[j]) { | ||
1609 | case '\0': | ||
1610 | loop = 0; | ||
1611 | /* Fall through */ | ||
1612 | case ',': | ||
1613 | bname[j] = '\0'; | ||
1614 | strcpy(dev->drv[drvidx]->msn2eaz[i], bname); | ||
1615 | j = ISDN_MSNLEN; | ||
1616 | break; | ||
1617 | default: | ||
1618 | j++; | ||
1619 | } | ||
1620 | if (j >= ISDN_MSNLEN) | ||
1621 | break; | ||
1622 | } | ||
1623 | if (++i > 9) | ||
1624 | break; | ||
1625 | } | ||
1626 | } else { | ||
1627 | p = (char __user *) iocts.arg; | ||
1628 | for (i = 0; i < 10; i++) { | ||
1629 | snprintf(bname, sizeof(bname), "%s%s", | ||
1630 | strlen(dev->drv[drvidx]->msn2eaz[i]) ? | ||
1631 | dev->drv[drvidx]->msn2eaz[i] : "_", | ||
1632 | (i < 9) ? "," : "\0"); | ||
1633 | if (copy_to_user(p, bname, strlen(bname) + 1)) | ||
1634 | return -EFAULT; | ||
1635 | p += strlen(bname); | ||
1636 | } | ||
1637 | } | ||
1638 | return 0; | ||
1639 | } else | ||
1640 | return -EINVAL; | ||
1641 | case IIOCDBGVAR: | ||
1642 | return -EINVAL; | ||
1643 | default: | ||
1644 | if ((cmd & IIOCDRVCTL) == IIOCDRVCTL) | ||
1645 | cmd = ((cmd >> _IOC_NRSHIFT) & _IOC_NRMASK) & ISDN_DRVIOCTL_MASK; | ||
1646 | else | ||
1647 | return -EINVAL; | ||
1648 | if (arg) { | ||
1649 | int i; | ||
1650 | char *p; | ||
1651 | if (copy_from_user(&iocts, argp, sizeof(isdn_ioctl_struct))) | ||
1652 | return -EFAULT; | ||
1653 | iocts.drvid[sizeof(iocts.drvid) - 1] = 0; | ||
1654 | if (strlen(iocts.drvid)) { | ||
1655 | if ((p = strchr(iocts.drvid, ','))) | ||
1656 | *p = 0; | ||
1657 | drvidx = -1; | ||
1658 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) | ||
1659 | if (!(strcmp(dev->drvid[i], iocts.drvid))) { | ||
1660 | drvidx = i; | ||
1661 | break; | ||
1662 | } | ||
1663 | } else | ||
1664 | drvidx = 0; | ||
1665 | if (drvidx == -1) | ||
1666 | return -ENODEV; | ||
1667 | c.driver = drvidx; | ||
1668 | c.command = ISDN_CMD_IOCTL; | ||
1669 | c.arg = cmd; | ||
1670 | memcpy(c.parm.num, &iocts.arg, sizeof(ulong)); | ||
1671 | ret = isdn_command(&c); | ||
1672 | memcpy(&iocts.arg, c.parm.num, sizeof(ulong)); | ||
1673 | if (copy_to_user(argp, &iocts, sizeof(isdn_ioctl_struct))) | ||
1674 | return -EFAULT; | ||
1675 | return ret; | ||
1676 | } else | ||
1677 | return -EINVAL; | ||
1678 | } | ||
1679 | } | ||
1680 | #ifdef CONFIG_ISDN_PPP | ||
1681 | if (minor <= ISDN_MINOR_PPPMAX) | ||
1682 | return (isdn_ppp_ioctl(minor - ISDN_MINOR_PPP, file, cmd, arg)); | ||
1683 | #endif | ||
1684 | return -ENODEV; | ||
1685 | |||
1686 | #undef name | ||
1687 | #undef bname | ||
1688 | #undef iocts | ||
1689 | #undef phone | ||
1690 | #undef cfg | ||
1691 | } | ||
1692 | |||
1693 | static long | ||
1694 | isdn_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
1695 | { | ||
1696 | int ret; | ||
1697 | |||
1698 | mutex_lock(&isdn_mutex); | ||
1699 | ret = isdn_ioctl(file, cmd, arg); | ||
1700 | mutex_unlock(&isdn_mutex); | ||
1701 | |||
1702 | return ret; | ||
1703 | } | ||
1704 | |||
1705 | /* | ||
1706 | * Open the device code. | ||
1707 | */ | ||
1708 | static int | ||
1709 | isdn_open(struct inode *ino, struct file *filep) | ||
1710 | { | ||
1711 | uint minor = iminor(ino); | ||
1712 | int drvidx; | ||
1713 | int chidx; | ||
1714 | int retval = -ENODEV; | ||
1715 | |||
1716 | mutex_lock(&isdn_mutex); | ||
1717 | if (minor == ISDN_MINOR_STATUS) { | ||
1718 | infostruct *p; | ||
1719 | |||
1720 | if ((p = kmalloc(sizeof(infostruct), GFP_KERNEL))) { | ||
1721 | p->next = (char *) dev->infochain; | ||
1722 | p->private = (char *) &(filep->private_data); | ||
1723 | dev->infochain = p; | ||
1724 | /* At opening we allow a single update */ | ||
1725 | filep->private_data = (char *) 1; | ||
1726 | retval = 0; | ||
1727 | goto out; | ||
1728 | } else { | ||
1729 | retval = -ENOMEM; | ||
1730 | goto out; | ||
1731 | } | ||
1732 | } | ||
1733 | if (!dev->channels) | ||
1734 | goto out; | ||
1735 | if (minor <= ISDN_MINOR_BMAX) { | ||
1736 | printk(KERN_WARNING "isdn_open minor %d obsolete!\n", minor); | ||
1737 | drvidx = isdn_minor2drv(minor); | ||
1738 | if (drvidx < 0) | ||
1739 | goto out; | ||
1740 | chidx = isdn_minor2chan(minor); | ||
1741 | if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) | ||
1742 | goto out; | ||
1743 | if (!(dev->drv[drvidx]->online & (1 << chidx))) | ||
1744 | goto out; | ||
1745 | isdn_lock_drivers(); | ||
1746 | retval = 0; | ||
1747 | goto out; | ||
1748 | } | ||
1749 | if (minor <= ISDN_MINOR_CTRLMAX) { | ||
1750 | drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); | ||
1751 | if (drvidx < 0) | ||
1752 | goto out; | ||
1753 | isdn_lock_drivers(); | ||
1754 | retval = 0; | ||
1755 | goto out; | ||
1756 | } | ||
1757 | #ifdef CONFIG_ISDN_PPP | ||
1758 | if (minor <= ISDN_MINOR_PPPMAX) { | ||
1759 | retval = isdn_ppp_open(minor - ISDN_MINOR_PPP, filep); | ||
1760 | if (retval == 0) | ||
1761 | isdn_lock_drivers(); | ||
1762 | goto out; | ||
1763 | } | ||
1764 | #endif | ||
1765 | out: | ||
1766 | nonseekable_open(ino, filep); | ||
1767 | mutex_unlock(&isdn_mutex); | ||
1768 | return retval; | ||
1769 | } | ||
1770 | |||
1771 | static int | ||
1772 | isdn_close(struct inode *ino, struct file *filep) | ||
1773 | { | ||
1774 | uint minor = iminor(ino); | ||
1775 | |||
1776 | mutex_lock(&isdn_mutex); | ||
1777 | if (minor == ISDN_MINOR_STATUS) { | ||
1778 | infostruct *p = dev->infochain; | ||
1779 | infostruct *q = NULL; | ||
1780 | |||
1781 | while (p) { | ||
1782 | if (p->private == (char *) &(filep->private_data)) { | ||
1783 | if (q) | ||
1784 | q->next = p->next; | ||
1785 | else | ||
1786 | dev->infochain = (infostruct *) (p->next); | ||
1787 | kfree(p); | ||
1788 | goto out; | ||
1789 | } | ||
1790 | q = p; | ||
1791 | p = (infostruct *) (p->next); | ||
1792 | } | ||
1793 | printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n"); | ||
1794 | goto out; | ||
1795 | } | ||
1796 | isdn_unlock_drivers(); | ||
1797 | if (minor <= ISDN_MINOR_BMAX) | ||
1798 | goto out; | ||
1799 | if (minor <= ISDN_MINOR_CTRLMAX) { | ||
1800 | if (dev->profd == current) | ||
1801 | dev->profd = NULL; | ||
1802 | goto out; | ||
1803 | } | ||
1804 | #ifdef CONFIG_ISDN_PPP | ||
1805 | if (minor <= ISDN_MINOR_PPPMAX) | ||
1806 | isdn_ppp_release(minor - ISDN_MINOR_PPP, filep); | ||
1807 | #endif | ||
1808 | |||
1809 | out: | ||
1810 | mutex_unlock(&isdn_mutex); | ||
1811 | return 0; | ||
1812 | } | ||
1813 | |||
1814 | static const struct file_operations isdn_fops = | ||
1815 | { | ||
1816 | .owner = THIS_MODULE, | ||
1817 | .llseek = no_llseek, | ||
1818 | .read = isdn_read, | ||
1819 | .write = isdn_write, | ||
1820 | .poll = isdn_poll, | ||
1821 | .unlocked_ioctl = isdn_unlocked_ioctl, | ||
1822 | .open = isdn_open, | ||
1823 | .release = isdn_close, | ||
1824 | }; | ||
1825 | |||
1826 | char * | ||
1827 | isdn_map_eaz2msn(char *msn, int di) | ||
1828 | { | ||
1829 | isdn_driver_t *this = dev->drv[di]; | ||
1830 | int i; | ||
1831 | |||
1832 | if (strlen(msn) == 1) { | ||
1833 | i = msn[0] - '0'; | ||
1834 | if ((i >= 0) && (i <= 9)) | ||
1835 | if (strlen(this->msn2eaz[i])) | ||
1836 | return (this->msn2eaz[i]); | ||
1837 | } | ||
1838 | return (msn); | ||
1839 | } | ||
1840 | |||
1841 | /* | ||
1842 | * Find an unused ISDN-channel, whose feature-flags match the | ||
1843 | * given L2- and L3-protocols. | ||
1844 | */ | ||
1845 | #define L2V (~(ISDN_FEATURE_L2_V11096 | ISDN_FEATURE_L2_V11019 | ISDN_FEATURE_L2_V11038)) | ||
1846 | |||
1847 | /* | ||
1848 | * This function must be called with holding the dev->lock. | ||
1849 | */ | ||
1850 | int | ||
1851 | isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev | ||
1852 | , int pre_chan, char *msn) | ||
1853 | { | ||
1854 | int i; | ||
1855 | ulong features; | ||
1856 | ulong vfeatures; | ||
1857 | |||
1858 | features = ((1 << l2_proto) | (0x10000 << l3_proto)); | ||
1859 | vfeatures = (((1 << l2_proto) | (0x10000 << l3_proto)) & | ||
1860 | ~(ISDN_FEATURE_L2_V11096 | ISDN_FEATURE_L2_V11019 | ISDN_FEATURE_L2_V11038)); | ||
1861 | /* If Layer-2 protocol is V.110, accept drivers with | ||
1862 | * transparent feature even if these don't support V.110 | ||
1863 | * because we can emulate this in linklevel. | ||
1864 | */ | ||
1865 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
1866 | if (USG_NONE(dev->usage[i]) && | ||
1867 | (dev->drvmap[i] != -1)) { | ||
1868 | int d = dev->drvmap[i]; | ||
1869 | if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) && | ||
1870 | ((pre_dev != d) || (pre_chan != dev->chanmap[i]))) | ||
1871 | continue; | ||
1872 | if (!strcmp(isdn_map_eaz2msn(msn, d), "-")) | ||
1873 | continue; | ||
1874 | if (dev->usage[i] & ISDN_USAGE_DISABLED) | ||
1875 | continue; /* usage not allowed */ | ||
1876 | if (dev->drv[d]->flags & DRV_FLAG_RUNNING) { | ||
1877 | if (((dev->drv[d]->interface->features & features) == features) || | ||
1878 | (((dev->drv[d]->interface->features & vfeatures) == vfeatures) && | ||
1879 | (dev->drv[d]->interface->features & ISDN_FEATURE_L2_TRANS))) { | ||
1880 | if ((pre_dev < 0) || (pre_chan < 0)) { | ||
1881 | dev->usage[i] &= ISDN_USAGE_EXCLUSIVE; | ||
1882 | dev->usage[i] |= usage; | ||
1883 | isdn_info_update(); | ||
1884 | return i; | ||
1885 | } else { | ||
1886 | if ((pre_dev == d) && (pre_chan == dev->chanmap[i])) { | ||
1887 | dev->usage[i] &= ISDN_USAGE_EXCLUSIVE; | ||
1888 | dev->usage[i] |= usage; | ||
1889 | isdn_info_update(); | ||
1890 | return i; | ||
1891 | } | ||
1892 | } | ||
1893 | } | ||
1894 | } | ||
1895 | } | ||
1896 | return -1; | ||
1897 | } | ||
1898 | |||
1899 | /* | ||
1900 | * Set state of ISDN-channel to 'unused' | ||
1901 | */ | ||
1902 | void | ||
1903 | isdn_free_channel(int di, int ch, int usage) | ||
1904 | { | ||
1905 | int i; | ||
1906 | |||
1907 | if ((di < 0) || (ch < 0)) { | ||
1908 | printk(KERN_WARNING "%s: called with invalid drv(%d) or channel(%d)\n", | ||
1909 | __func__, di, ch); | ||
1910 | return; | ||
1911 | } | ||
1912 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
1913 | if (((!usage) || ((dev->usage[i] & ISDN_USAGE_MASK) == usage)) && | ||
1914 | (dev->drvmap[i] == di) && | ||
1915 | (dev->chanmap[i] == ch)) { | ||
1916 | dev->usage[i] &= (ISDN_USAGE_NONE | ISDN_USAGE_EXCLUSIVE); | ||
1917 | strcpy(dev->num[i], "???"); | ||
1918 | dev->ibytes[i] = 0; | ||
1919 | dev->obytes[i] = 0; | ||
1920 | // 20.10.99 JIM, try to reinitialize v110 ! | ||
1921 | dev->v110emu[i] = 0; | ||
1922 | atomic_set(&(dev->v110use[i]), 0); | ||
1923 | isdn_v110_close(dev->v110[i]); | ||
1924 | dev->v110[i] = NULL; | ||
1925 | // 20.10.99 JIM, try to reinitialize v110 ! | ||
1926 | isdn_info_update(); | ||
1927 | if (dev->drv[di]) | ||
1928 | skb_queue_purge(&dev->drv[di]->rpqueue[ch]); | ||
1929 | } | ||
1930 | } | ||
1931 | |||
1932 | /* | ||
1933 | * Cancel Exclusive-Flag for ISDN-channel | ||
1934 | */ | ||
1935 | void | ||
1936 | isdn_unexclusive_channel(int di, int ch) | ||
1937 | { | ||
1938 | int i; | ||
1939 | |||
1940 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
1941 | if ((dev->drvmap[i] == di) && | ||
1942 | (dev->chanmap[i] == ch)) { | ||
1943 | dev->usage[i] &= ~ISDN_USAGE_EXCLUSIVE; | ||
1944 | isdn_info_update(); | ||
1945 | return; | ||
1946 | } | ||
1947 | } | ||
1948 | |||
1949 | /* | ||
1950 | * writebuf replacement for SKB_ABLE drivers | ||
1951 | */ | ||
1952 | static int | ||
1953 | isdn_writebuf_stub(int drvidx, int chan, const u_char __user *buf, int len) | ||
1954 | { | ||
1955 | int ret; | ||
1956 | int hl = dev->drv[drvidx]->interface->hl_hdrlen; | ||
1957 | struct sk_buff *skb = alloc_skb(hl + len, GFP_ATOMIC); | ||
1958 | |||
1959 | if (!skb) | ||
1960 | return -ENOMEM; | ||
1961 | skb_reserve(skb, hl); | ||
1962 | if (copy_from_user(skb_put(skb, len), buf, len)) { | ||
1963 | dev_kfree_skb(skb); | ||
1964 | return -EFAULT; | ||
1965 | } | ||
1966 | ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, 1, skb); | ||
1967 | if (ret <= 0) | ||
1968 | dev_kfree_skb(skb); | ||
1969 | if (ret > 0) | ||
1970 | dev->obytes[isdn_dc2minor(drvidx, chan)] += ret; | ||
1971 | return ret; | ||
1972 | } | ||
1973 | |||
1974 | /* | ||
1975 | * Return: length of data on success, -ERRcode on failure. | ||
1976 | */ | ||
1977 | int | ||
1978 | isdn_writebuf_skb_stub(int drvidx, int chan, int ack, struct sk_buff *skb) | ||
1979 | { | ||
1980 | int ret; | ||
1981 | struct sk_buff *nskb = NULL; | ||
1982 | int v110_ret = skb->len; | ||
1983 | int idx = isdn_dc2minor(drvidx, chan); | ||
1984 | |||
1985 | if (dev->v110[idx]) { | ||
1986 | atomic_inc(&dev->v110use[idx]); | ||
1987 | nskb = isdn_v110_encode(dev->v110[idx], skb); | ||
1988 | atomic_dec(&dev->v110use[idx]); | ||
1989 | if (!nskb) | ||
1990 | return 0; | ||
1991 | v110_ret = *((int *)nskb->data); | ||
1992 | skb_pull(nskb, sizeof(int)); | ||
1993 | if (!nskb->len) { | ||
1994 | dev_kfree_skb(nskb); | ||
1995 | return v110_ret; | ||
1996 | } | ||
1997 | /* V.110 must always be acknowledged */ | ||
1998 | ack = 1; | ||
1999 | ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, nskb); | ||
2000 | } else { | ||
2001 | int hl = dev->drv[drvidx]->interface->hl_hdrlen; | ||
2002 | |||
2003 | if (skb_headroom(skb) < hl) { | ||
2004 | /* | ||
2005 | * This should only occur when new HL driver with | ||
2006 | * increased hl_hdrlen was loaded after netdevice | ||
2007 | * was created and connected to the new driver. | ||
2008 | * | ||
2009 | * The V.110 branch (re-allocates on its own) does | ||
2010 | * not need this | ||
2011 | */ | ||
2012 | struct sk_buff *skb_tmp; | ||
2013 | |||
2014 | skb_tmp = skb_realloc_headroom(skb, hl); | ||
2015 | printk(KERN_DEBUG "isdn_writebuf_skb_stub: reallocating headroom%s\n", skb_tmp ? "" : " failed"); | ||
2016 | if (!skb_tmp) return -ENOMEM; /* 0 better? */ | ||
2017 | ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb_tmp); | ||
2018 | if (ret > 0) { | ||
2019 | dev_kfree_skb(skb); | ||
2020 | } else { | ||
2021 | dev_kfree_skb(skb_tmp); | ||
2022 | } | ||
2023 | } else { | ||
2024 | ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb); | ||
2025 | } | ||
2026 | } | ||
2027 | if (ret > 0) { | ||
2028 | dev->obytes[idx] += ret; | ||
2029 | if (dev->v110[idx]) { | ||
2030 | atomic_inc(&dev->v110use[idx]); | ||
2031 | dev->v110[idx]->skbuser++; | ||
2032 | atomic_dec(&dev->v110use[idx]); | ||
2033 | /* For V.110 return unencoded data length */ | ||
2034 | ret = v110_ret; | ||
2035 | /* if the complete frame was send we free the skb; | ||
2036 | if not upper function will requeue the skb */ | ||
2037 | if (ret == skb->len) | ||
2038 | dev_kfree_skb(skb); | ||
2039 | } | ||
2040 | } else | ||
2041 | if (dev->v110[idx]) | ||
2042 | dev_kfree_skb(nskb); | ||
2043 | return ret; | ||
2044 | } | ||
2045 | |||
2046 | static int | ||
2047 | isdn_add_channels(isdn_driver_t *d, int drvidx, int n, int adding) | ||
2048 | { | ||
2049 | int j, k, m; | ||
2050 | |||
2051 | init_waitqueue_head(&d->st_waitq); | ||
2052 | if (d->flags & DRV_FLAG_RUNNING) | ||
2053 | return -1; | ||
2054 | if (n < 1) return 0; | ||
2055 | |||
2056 | m = (adding) ? d->channels + n : n; | ||
2057 | |||
2058 | if (dev->channels + n > ISDN_MAX_CHANNELS) { | ||
2059 | printk(KERN_WARNING "register_isdn: Max. %d channels supported\n", | ||
2060 | ISDN_MAX_CHANNELS); | ||
2061 | return -1; | ||
2062 | } | ||
2063 | |||
2064 | if ((adding) && (d->rcverr)) | ||
2065 | kfree(d->rcverr); | ||
2066 | if (!(d->rcverr = kcalloc(m, sizeof(int), GFP_ATOMIC))) { | ||
2067 | printk(KERN_WARNING "register_isdn: Could not alloc rcverr\n"); | ||
2068 | return -1; | ||
2069 | } | ||
2070 | |||
2071 | if ((adding) && (d->rcvcount)) | ||
2072 | kfree(d->rcvcount); | ||
2073 | if (!(d->rcvcount = kcalloc(m, sizeof(int), GFP_ATOMIC))) { | ||
2074 | printk(KERN_WARNING "register_isdn: Could not alloc rcvcount\n"); | ||
2075 | if (!adding) | ||
2076 | kfree(d->rcverr); | ||
2077 | return -1; | ||
2078 | } | ||
2079 | |||
2080 | if ((adding) && (d->rpqueue)) { | ||
2081 | for (j = 0; j < d->channels; j++) | ||
2082 | skb_queue_purge(&d->rpqueue[j]); | ||
2083 | kfree(d->rpqueue); | ||
2084 | } | ||
2085 | d->rpqueue = kmalloc_array(m, sizeof(struct sk_buff_head), GFP_ATOMIC); | ||
2086 | if (!d->rpqueue) { | ||
2087 | printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n"); | ||
2088 | if (!adding) { | ||
2089 | kfree(d->rcvcount); | ||
2090 | kfree(d->rcverr); | ||
2091 | } | ||
2092 | return -1; | ||
2093 | } | ||
2094 | for (j = 0; j < m; j++) { | ||
2095 | skb_queue_head_init(&d->rpqueue[j]); | ||
2096 | } | ||
2097 | |||
2098 | if ((adding) && (d->rcv_waitq)) | ||
2099 | kfree(d->rcv_waitq); | ||
2100 | d->rcv_waitq = kmalloc(array3_size(sizeof(wait_queue_head_t), 2, m), | ||
2101 | GFP_ATOMIC); | ||
2102 | if (!d->rcv_waitq) { | ||
2103 | printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n"); | ||
2104 | if (!adding) { | ||
2105 | kfree(d->rpqueue); | ||
2106 | kfree(d->rcvcount); | ||
2107 | kfree(d->rcverr); | ||
2108 | } | ||
2109 | return -1; | ||
2110 | } | ||
2111 | d->snd_waitq = d->rcv_waitq + m; | ||
2112 | for (j = 0; j < m; j++) { | ||
2113 | init_waitqueue_head(&d->rcv_waitq[j]); | ||
2114 | init_waitqueue_head(&d->snd_waitq[j]); | ||
2115 | } | ||
2116 | |||
2117 | dev->channels += n; | ||
2118 | for (j = d->channels; j < m; j++) | ||
2119 | for (k = 0; k < ISDN_MAX_CHANNELS; k++) | ||
2120 | if (dev->chanmap[k] < 0) { | ||
2121 | dev->chanmap[k] = j; | ||
2122 | dev->drvmap[k] = drvidx; | ||
2123 | break; | ||
2124 | } | ||
2125 | d->channels = m; | ||
2126 | return 0; | ||
2127 | } | ||
2128 | |||
2129 | /* | ||
2130 | * Low-level-driver registration | ||
2131 | */ | ||
2132 | |||
2133 | static void | ||
2134 | set_global_features(void) | ||
2135 | { | ||
2136 | int drvidx; | ||
2137 | |||
2138 | dev->global_features = 0; | ||
2139 | for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++) { | ||
2140 | if (!dev->drv[drvidx]) | ||
2141 | continue; | ||
2142 | if (dev->drv[drvidx]->interface) | ||
2143 | dev->global_features |= dev->drv[drvidx]->interface->features; | ||
2144 | } | ||
2145 | } | ||
2146 | |||
2147 | #ifdef CONFIG_ISDN_DIVERSION | ||
2148 | |||
2149 | static char *map_drvname(int di) | ||
2150 | { | ||
2151 | if ((di < 0) || (di >= ISDN_MAX_DRIVERS)) | ||
2152 | return (NULL); | ||
2153 | return (dev->drvid[di]); /* driver name */ | ||
2154 | } /* map_drvname */ | ||
2155 | |||
2156 | static int map_namedrv(char *id) | ||
2157 | { int i; | ||
2158 | |||
2159 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) | ||
2160 | { if (!strcmp(dev->drvid[i], id)) | ||
2161 | return (i); | ||
2162 | } | ||
2163 | return (-1); | ||
2164 | } /* map_namedrv */ | ||
2165 | |||
2166 | int DIVERT_REG_NAME(isdn_divert_if *i_div) | ||
2167 | { | ||
2168 | if (i_div->if_magic != DIVERT_IF_MAGIC) | ||
2169 | return (DIVERT_VER_ERR); | ||
2170 | switch (i_div->cmd) | ||
2171 | { | ||
2172 | case DIVERT_CMD_REL: | ||
2173 | if (divert_if != i_div) | ||
2174 | return (DIVERT_REL_ERR); | ||
2175 | divert_if = NULL; /* free interface */ | ||
2176 | return (DIVERT_NO_ERR); | ||
2177 | |||
2178 | case DIVERT_CMD_REG: | ||
2179 | if (divert_if) | ||
2180 | return (DIVERT_REG_ERR); | ||
2181 | i_div->ll_cmd = isdn_command; /* set command function */ | ||
2182 | i_div->drv_to_name = map_drvname; | ||
2183 | i_div->name_to_drv = map_namedrv; | ||
2184 | divert_if = i_div; /* remember interface */ | ||
2185 | return (DIVERT_NO_ERR); | ||
2186 | |||
2187 | default: | ||
2188 | return (DIVERT_CMD_ERR); | ||
2189 | } | ||
2190 | } /* DIVERT_REG_NAME */ | ||
2191 | |||
2192 | EXPORT_SYMBOL(DIVERT_REG_NAME); | ||
2193 | |||
2194 | #endif /* CONFIG_ISDN_DIVERSION */ | ||
2195 | |||
2196 | |||
2197 | EXPORT_SYMBOL(register_isdn); | ||
2198 | #ifdef CONFIG_ISDN_PPP | ||
2199 | EXPORT_SYMBOL(isdn_ppp_register_compressor); | ||
2200 | EXPORT_SYMBOL(isdn_ppp_unregister_compressor); | ||
2201 | #endif | ||
2202 | |||
2203 | int | ||
2204 | register_isdn(isdn_if *i) | ||
2205 | { | ||
2206 | isdn_driver_t *d; | ||
2207 | int j; | ||
2208 | ulong flags; | ||
2209 | int drvidx; | ||
2210 | |||
2211 | if (dev->drivers >= ISDN_MAX_DRIVERS) { | ||
2212 | printk(KERN_WARNING "register_isdn: Max. %d drivers supported\n", | ||
2213 | ISDN_MAX_DRIVERS); | ||
2214 | return 0; | ||
2215 | } | ||
2216 | if (!i->writebuf_skb) { | ||
2217 | printk(KERN_WARNING "register_isdn: No write routine given.\n"); | ||
2218 | return 0; | ||
2219 | } | ||
2220 | if (!(d = kzalloc(sizeof(isdn_driver_t), GFP_KERNEL))) { | ||
2221 | printk(KERN_WARNING "register_isdn: Could not alloc driver-struct\n"); | ||
2222 | return 0; | ||
2223 | } | ||
2224 | |||
2225 | d->maxbufsize = i->maxbufsize; | ||
2226 | d->pktcount = 0; | ||
2227 | d->stavail = 0; | ||
2228 | d->flags = DRV_FLAG_LOADED; | ||
2229 | d->online = 0; | ||
2230 | d->interface = i; | ||
2231 | d->channels = 0; | ||
2232 | spin_lock_irqsave(&dev->lock, flags); | ||
2233 | for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++) | ||
2234 | if (!dev->drv[drvidx]) | ||
2235 | break; | ||
2236 | if (isdn_add_channels(d, drvidx, i->channels, 0)) { | ||
2237 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2238 | kfree(d); | ||
2239 | return 0; | ||
2240 | } | ||
2241 | i->channels = drvidx; | ||
2242 | i->rcvcallb_skb = isdn_receive_skb_callback; | ||
2243 | i->statcallb = isdn_status_callback; | ||
2244 | if (!strlen(i->id)) | ||
2245 | sprintf(i->id, "line%d", drvidx); | ||
2246 | for (j = 0; j < drvidx; j++) | ||
2247 | if (!strcmp(i->id, dev->drvid[j])) | ||
2248 | sprintf(i->id, "line%d", drvidx); | ||
2249 | dev->drv[drvidx] = d; | ||
2250 | strcpy(dev->drvid[drvidx], i->id); | ||
2251 | isdn_info_update(); | ||
2252 | dev->drivers++; | ||
2253 | set_global_features(); | ||
2254 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2255 | return 1; | ||
2256 | } | ||
2257 | |||
2258 | /* | ||
2259 | ***************************************************************************** | ||
2260 | * And now the modules code. | ||
2261 | ***************************************************************************** | ||
2262 | */ | ||
2263 | |||
2264 | static char * | ||
2265 | isdn_getrev(const char *revision) | ||
2266 | { | ||
2267 | char *rev; | ||
2268 | char *p; | ||
2269 | |||
2270 | if ((p = strchr(revision, ':'))) { | ||
2271 | rev = p + 2; | ||
2272 | p = strchr(rev, '$'); | ||
2273 | *--p = 0; | ||
2274 | } else | ||
2275 | rev = "???"; | ||
2276 | return rev; | ||
2277 | } | ||
2278 | |||
2279 | /* | ||
2280 | * Allocate and initialize all data, register modem-devices | ||
2281 | */ | ||
2282 | static int __init isdn_init(void) | ||
2283 | { | ||
2284 | int i; | ||
2285 | char tmprev[50]; | ||
2286 | |||
2287 | dev = vzalloc(sizeof(isdn_dev)); | ||
2288 | if (!dev) { | ||
2289 | printk(KERN_WARNING "isdn: Could not allocate device-struct.\n"); | ||
2290 | return -EIO; | ||
2291 | } | ||
2292 | timer_setup(&dev->timer, isdn_timer_funct, 0); | ||
2293 | spin_lock_init(&dev->lock); | ||
2294 | spin_lock_init(&dev->timerlock); | ||
2295 | #ifdef MODULE | ||
2296 | dev->owner = THIS_MODULE; | ||
2297 | #endif | ||
2298 | mutex_init(&dev->mtx); | ||
2299 | init_waitqueue_head(&dev->info_waitq); | ||
2300 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
2301 | dev->drvmap[i] = -1; | ||
2302 | dev->chanmap[i] = -1; | ||
2303 | dev->m_idx[i] = -1; | ||
2304 | strcpy(dev->num[i], "???"); | ||
2305 | } | ||
2306 | if (register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) { | ||
2307 | printk(KERN_WARNING "isdn: Could not register control devices\n"); | ||
2308 | vfree(dev); | ||
2309 | return -EIO; | ||
2310 | } | ||
2311 | if ((isdn_tty_modem_init()) < 0) { | ||
2312 | printk(KERN_WARNING "isdn: Could not register tty devices\n"); | ||
2313 | vfree(dev); | ||
2314 | unregister_chrdev(ISDN_MAJOR, "isdn"); | ||
2315 | return -EIO; | ||
2316 | } | ||
2317 | #ifdef CONFIG_ISDN_PPP | ||
2318 | if (isdn_ppp_init() < 0) { | ||
2319 | printk(KERN_WARNING "isdn: Could not create PPP-device-structs\n"); | ||
2320 | isdn_tty_exit(); | ||
2321 | unregister_chrdev(ISDN_MAJOR, "isdn"); | ||
2322 | vfree(dev); | ||
2323 | return -EIO; | ||
2324 | } | ||
2325 | #endif /* CONFIG_ISDN_PPP */ | ||
2326 | |||
2327 | strcpy(tmprev, isdn_revision); | ||
2328 | printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(tmprev)); | ||
2329 | strcpy(tmprev, isdn_net_revision); | ||
2330 | printk("%s/", isdn_getrev(tmprev)); | ||
2331 | strcpy(tmprev, isdn_ppp_revision); | ||
2332 | printk("%s/", isdn_getrev(tmprev)); | ||
2333 | strcpy(tmprev, isdn_audio_revision); | ||
2334 | printk("%s/", isdn_getrev(tmprev)); | ||
2335 | strcpy(tmprev, isdn_v110_revision); | ||
2336 | printk("%s", isdn_getrev(tmprev)); | ||
2337 | |||
2338 | #ifdef MODULE | ||
2339 | printk(" loaded\n"); | ||
2340 | #else | ||
2341 | printk("\n"); | ||
2342 | #endif | ||
2343 | isdn_info_update(); | ||
2344 | return 0; | ||
2345 | } | ||
2346 | |||
2347 | /* | ||
2348 | * Unload module | ||
2349 | */ | ||
2350 | static void __exit isdn_exit(void) | ||
2351 | { | ||
2352 | #ifdef CONFIG_ISDN_PPP | ||
2353 | isdn_ppp_cleanup(); | ||
2354 | #endif | ||
2355 | if (isdn_net_rmall() < 0) { | ||
2356 | printk(KERN_WARNING "isdn: net-device busy, remove cancelled\n"); | ||
2357 | return; | ||
2358 | } | ||
2359 | isdn_tty_exit(); | ||
2360 | unregister_chrdev(ISDN_MAJOR, "isdn"); | ||
2361 | del_timer_sync(&dev->timer); | ||
2362 | /* call vfree with interrupts enabled, else it will hang */ | ||
2363 | vfree(dev); | ||
2364 | printk(KERN_NOTICE "ISDN-subsystem unloaded\n"); | ||
2365 | } | ||
2366 | |||
2367 | module_init(isdn_init); | ||
2368 | module_exit(isdn_exit); | ||
diff --git a/drivers/isdn/i4l/isdn_common.h b/drivers/isdn/i4l/isdn_common.h deleted file mode 100644 index 2260ef07ab9c..000000000000 --- a/drivers/isdn/i4l/isdn_common.h +++ /dev/null | |||
@@ -1,47 +0,0 @@ | |||
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_readbchan_tty(int, int, struct tty_port *, int); | ||
41 | extern int isdn_get_free_channel(int, int, int, int, int, char *); | ||
42 | extern int isdn_writebuf_skb_stub(int, int, int, struct sk_buff *); | ||
43 | extern int register_isdn(isdn_if *i); | ||
44 | extern int isdn_msncmp(const char *, const char *); | ||
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 deleted file mode 100644 index 336523ec077c..000000000000 --- a/drivers/isdn/i4l/isdn_concap.c +++ /dev/null | |||
@@ -1,99 +0,0 @@ | |||
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 | static 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 *) netdev_priv(ndev))->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 | static int isdn_concap_dl_connect_req(struct concap_proto *concap) | ||
62 | { | ||
63 | struct net_device *ndev = concap->net_dev; | ||
64 | isdn_net_local *lp = netdev_priv(ndev); | ||
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 | static 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 | .data_req = &isdn_concap_dl_data_req, | ||
84 | .connect_req = &isdn_concap_dl_connect_req, | ||
85 | .disconn_req = &isdn_concap_dl_disconn_req | ||
86 | }; | ||
87 | |||
88 | /* The following should better go into a dedicated source file such that | ||
89 | this sourcefile does not need to include any protocol specific header | ||
90 | files. For now: | ||
91 | */ | ||
92 | struct concap_proto *isdn_concap_new(int encap) | ||
93 | { | ||
94 | switch (encap) { | ||
95 | case ISDN_NET_ENCAP_X25IFACE: | ||
96 | return isdn_x25iface_proto_new(); | ||
97 | } | ||
98 | return NULL; | ||
99 | } | ||
diff --git a/drivers/isdn/i4l/isdn_concap.h b/drivers/isdn/i4l/isdn_concap.h deleted file mode 100644 index cd7e3ba74e25..000000000000 --- a/drivers/isdn/i4l/isdn_concap.h +++ /dev/null | |||
@@ -1,11 +0,0 @@ | |||
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_proto *isdn_concap_new(int); | ||
diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c deleted file mode 100644 index c138f66f2659..000000000000 --- a/drivers/isdn/i4l/isdn_net.c +++ /dev/null | |||
@@ -1,3198 +0,0 @@ | |||
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/isdn.h> | ||
26 | #include <linux/slab.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 it 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_MASTER_PRIV(lp)->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 | static int isdn_net_force_dial_lp(isdn_net_local *); | ||
180 | static netdev_tx_t isdn_net_start_xmit(struct sk_buff *, | ||
181 | struct net_device *); | ||
182 | |||
183 | static void isdn_net_ciscohdlck_connected(isdn_net_local *lp); | ||
184 | static void isdn_net_ciscohdlck_disconnected(isdn_net_local *lp); | ||
185 | |||
186 | char *isdn_net_revision = "$Revision: 1.1.2.2 $"; | ||
187 | |||
188 | /* | ||
189 | * Code for raw-networking over ISDN | ||
190 | */ | ||
191 | |||
192 | static void | ||
193 | isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason) | ||
194 | { | ||
195 | if (skb) { | ||
196 | |||
197 | u_short proto = ntohs(skb->protocol); | ||
198 | |||
199 | printk(KERN_DEBUG "isdn_net: %s: %s, signalling dst_link_failure %s\n", | ||
200 | dev->name, | ||
201 | (reason != NULL) ? reason : "unknown", | ||
202 | (proto != ETH_P_IP) ? "Protocol != ETH_P_IP" : ""); | ||
203 | |||
204 | dst_link_failure(skb); | ||
205 | } | ||
206 | else { /* dial not triggered by rawIP packet */ | ||
207 | printk(KERN_DEBUG "isdn_net: %s: %s\n", | ||
208 | dev->name, | ||
209 | (reason != NULL) ? reason : "reason unknown"); | ||
210 | } | ||
211 | } | ||
212 | |||
213 | static void | ||
214 | isdn_net_reset(struct net_device *dev) | ||
215 | { | ||
216 | #ifdef CONFIG_ISDN_X25 | ||
217 | struct concap_device_ops *dops = | ||
218 | ((isdn_net_local *)netdev_priv(dev))->dops; | ||
219 | struct concap_proto *cprot = | ||
220 | ((isdn_net_local *)netdev_priv(dev))->netdev->cprot; | ||
221 | #endif | ||
222 | #ifdef CONFIG_ISDN_X25 | ||
223 | if (cprot && cprot->pops && dops) | ||
224 | cprot->pops->restart(cprot, dev, dops); | ||
225 | #endif | ||
226 | } | ||
227 | |||
228 | /* Open/initialize the board. */ | ||
229 | static int | ||
230 | isdn_net_open(struct net_device *dev) | ||
231 | { | ||
232 | int i; | ||
233 | struct net_device *p; | ||
234 | struct in_device *in_dev; | ||
235 | |||
236 | /* moved here from isdn_net_reset, because only the master has an | ||
237 | interface associated which is supposed to be started. BTW: | ||
238 | we need to call netif_start_queue, not netif_wake_queue here */ | ||
239 | netif_start_queue(dev); | ||
240 | |||
241 | isdn_net_reset(dev); | ||
242 | /* Fill in the MAC-level header (not needed, but for compatibility... */ | ||
243 | for (i = 0; i < ETH_ALEN - sizeof(u32); i++) | ||
244 | dev->dev_addr[i] = 0xfc; | ||
245 | if ((in_dev = dev->ip_ptr) != NULL) { | ||
246 | /* | ||
247 | * Any address will do - we take the first | ||
248 | */ | ||
249 | struct in_ifaddr *ifa = in_dev->ifa_list; | ||
250 | if (ifa != NULL) | ||
251 | memcpy(dev->dev_addr + 2, &ifa->ifa_local, 4); | ||
252 | } | ||
253 | |||
254 | /* If this interface has slaves, start them also */ | ||
255 | p = MASTER_TO_SLAVE(dev); | ||
256 | if (p) { | ||
257 | while (p) { | ||
258 | isdn_net_reset(p); | ||
259 | p = MASTER_TO_SLAVE(p); | ||
260 | } | ||
261 | } | ||
262 | isdn_lock_drivers(); | ||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | /* | ||
267 | * Assign an ISDN-channel to a net-interface | ||
268 | */ | ||
269 | static void | ||
270 | isdn_net_bind_channel(isdn_net_local *lp, int idx) | ||
271 | { | ||
272 | lp->flags |= ISDN_NET_CONNECTED; | ||
273 | lp->isdn_device = dev->drvmap[idx]; | ||
274 | lp->isdn_channel = dev->chanmap[idx]; | ||
275 | dev->rx_netdev[idx] = lp->netdev; | ||
276 | dev->st_netdev[idx] = lp->netdev; | ||
277 | } | ||
278 | |||
279 | /* | ||
280 | * unbind a net-interface (resets interface after an error) | ||
281 | */ | ||
282 | static void | ||
283 | isdn_net_unbind_channel(isdn_net_local *lp) | ||
284 | { | ||
285 | skb_queue_purge(&lp->super_tx_queue); | ||
286 | |||
287 | if (!lp->master) { /* reset only master device */ | ||
288 | /* Moral equivalent of dev_purge_queues(): | ||
289 | BEWARE! This chunk of code cannot be called from hardware | ||
290 | interrupt handler. I hope it is true. --ANK | ||
291 | */ | ||
292 | qdisc_reset_all_tx(lp->netdev->dev); | ||
293 | } | ||
294 | lp->dialstate = 0; | ||
295 | dev->rx_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL; | ||
296 | dev->st_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL; | ||
297 | if (lp->isdn_device != -1 && lp->isdn_channel != -1) | ||
298 | isdn_free_channel(lp->isdn_device, lp->isdn_channel, | ||
299 | ISDN_USAGE_NET); | ||
300 | lp->flags &= ~ISDN_NET_CONNECTED; | ||
301 | lp->isdn_device = -1; | ||
302 | lp->isdn_channel = -1; | ||
303 | } | ||
304 | |||
305 | /* | ||
306 | * Perform auto-hangup and cps-calculation for net-interfaces. | ||
307 | * | ||
308 | * auto-hangup: | ||
309 | * Increment idle-counter (this counter is reset on any incoming or | ||
310 | * outgoing packet), if counter exceeds configured limit either do a | ||
311 | * hangup immediately or - if configured - wait until just before the next | ||
312 | * charge-info. | ||
313 | * | ||
314 | * cps-calculation (needed for dynamic channel-bundling): | ||
315 | * Since this function is called every second, simply reset the | ||
316 | * byte-counter of the interface after copying it to the cps-variable. | ||
317 | */ | ||
318 | static unsigned long last_jiffies = -HZ; | ||
319 | |||
320 | void | ||
321 | isdn_net_autohup(void) | ||
322 | { | ||
323 | isdn_net_dev *p = dev->netdev; | ||
324 | int anymore; | ||
325 | |||
326 | anymore = 0; | ||
327 | while (p) { | ||
328 | isdn_net_local *l = p->local; | ||
329 | if (jiffies == last_jiffies) | ||
330 | l->cps = l->transcount; | ||
331 | else | ||
332 | l->cps = (l->transcount * HZ) / (jiffies - last_jiffies); | ||
333 | l->transcount = 0; | ||
334 | if (dev->net_verbose > 3) | ||
335 | printk(KERN_DEBUG "%s: %d bogocps\n", p->dev->name, l->cps); | ||
336 | if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) { | ||
337 | anymore = 1; | ||
338 | l->huptimer++; | ||
339 | /* | ||
340 | * if there is some dialmode where timeout-hangup | ||
341 | * should _not_ be done, check for that here | ||
342 | */ | ||
343 | if ((l->onhtime) && | ||
344 | (l->huptimer > l->onhtime)) | ||
345 | { | ||
346 | if (l->hupflags & ISDN_MANCHARGE && | ||
347 | l->hupflags & ISDN_CHARGEHUP) { | ||
348 | while (time_after(jiffies, l->chargetime + l->chargeint)) | ||
349 | l->chargetime += l->chargeint; | ||
350 | if (time_after(jiffies, l->chargetime + l->chargeint - 2 * HZ)) | ||
351 | if (l->outgoing || l->hupflags & ISDN_INHUP) | ||
352 | isdn_net_hangup(p->dev); | ||
353 | } else if (l->outgoing) { | ||
354 | if (l->hupflags & ISDN_CHARGEHUP) { | ||
355 | if (l->hupflags & ISDN_WAITCHARGE) { | ||
356 | printk(KERN_DEBUG "isdn_net: Hupflags of %s are %X\n", | ||
357 | p->dev->name, l->hupflags); | ||
358 | isdn_net_hangup(p->dev); | ||
359 | } else if (time_after(jiffies, l->chargetime + l->chargeint)) { | ||
360 | printk(KERN_DEBUG | ||
361 | "isdn_net: %s: chtime = %lu, chint = %d\n", | ||
362 | p->dev->name, l->chargetime, l->chargeint); | ||
363 | isdn_net_hangup(p->dev); | ||
364 | } | ||
365 | } else | ||
366 | isdn_net_hangup(p->dev); | ||
367 | } else if (l->hupflags & ISDN_INHUP) | ||
368 | isdn_net_hangup(p->dev); | ||
369 | } | ||
370 | |||
371 | if (dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*l) == ISDN_NET_DM_OFF)) { | ||
372 | isdn_net_hangup(p->dev); | ||
373 | break; | ||
374 | } | ||
375 | } | ||
376 | p = (isdn_net_dev *) p->next; | ||
377 | } | ||
378 | last_jiffies = jiffies; | ||
379 | isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, anymore); | ||
380 | } | ||
381 | |||
382 | static void isdn_net_lp_disconnected(isdn_net_local *lp) | ||
383 | { | ||
384 | isdn_net_rm_from_bundle(lp); | ||
385 | } | ||
386 | |||
387 | /* | ||
388 | * Handle status-messages from ISDN-interfacecard. | ||
389 | * This function is called from within the main-status-dispatcher | ||
390 | * isdn_status_callback, which itself is called from the low-level driver. | ||
391 | * Return: 1 = Event handled, 0 = not for us or unknown Event. | ||
392 | */ | ||
393 | int | ||
394 | isdn_net_stat_callback(int idx, isdn_ctrl *c) | ||
395 | { | ||
396 | isdn_net_dev *p = dev->st_netdev[idx]; | ||
397 | int cmd = c->command; | ||
398 | |||
399 | if (p) { | ||
400 | isdn_net_local *lp = p->local; | ||
401 | #ifdef CONFIG_ISDN_X25 | ||
402 | struct concap_proto *cprot = lp->netdev->cprot; | ||
403 | struct concap_proto_ops *pops = cprot ? cprot->pops : NULL; | ||
404 | #endif | ||
405 | switch (cmd) { | ||
406 | case ISDN_STAT_BSENT: | ||
407 | /* A packet has successfully been sent out */ | ||
408 | if ((lp->flags & ISDN_NET_CONNECTED) && | ||
409 | (!lp->dialstate)) { | ||
410 | isdn_net_dec_frame_cnt(lp); | ||
411 | lp->stats.tx_packets++; | ||
412 | lp->stats.tx_bytes += c->parm.length; | ||
413 | } | ||
414 | return 1; | ||
415 | case ISDN_STAT_DCONN: | ||
416 | /* D-Channel is up */ | ||
417 | switch (lp->dialstate) { | ||
418 | case 4: | ||
419 | case 7: | ||
420 | case 8: | ||
421 | lp->dialstate++; | ||
422 | return 1; | ||
423 | case 12: | ||
424 | lp->dialstate = 5; | ||
425 | return 1; | ||
426 | } | ||
427 | break; | ||
428 | case ISDN_STAT_DHUP: | ||
429 | /* Either D-Channel-hangup or error during dialout */ | ||
430 | #ifdef CONFIG_ISDN_X25 | ||
431 | /* If we are not connencted then dialing had | ||
432 | failed. If there are generic encap protocol | ||
433 | receiver routines signal the closure of | ||
434 | the link*/ | ||
435 | |||
436 | if (!(lp->flags & ISDN_NET_CONNECTED) | ||
437 | && pops && pops->disconn_ind) | ||
438 | pops->disconn_ind(cprot); | ||
439 | #endif /* CONFIG_ISDN_X25 */ | ||
440 | if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) { | ||
441 | if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK) | ||
442 | isdn_net_ciscohdlck_disconnected(lp); | ||
443 | #ifdef CONFIG_ISDN_PPP | ||
444 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) | ||
445 | isdn_ppp_free(lp); | ||
446 | #endif | ||
447 | isdn_net_lp_disconnected(lp); | ||
448 | isdn_all_eaz(lp->isdn_device, lp->isdn_channel); | ||
449 | printk(KERN_INFO "%s: remote hangup\n", p->dev->name); | ||
450 | printk(KERN_INFO "%s: Chargesum is %d\n", p->dev->name, | ||
451 | lp->charge); | ||
452 | isdn_net_unbind_channel(lp); | ||
453 | return 1; | ||
454 | } | ||
455 | break; | ||
456 | #ifdef CONFIG_ISDN_X25 | ||
457 | case ISDN_STAT_BHUP: | ||
458 | /* B-Channel-hangup */ | ||
459 | /* try if there are generic encap protocol | ||
460 | receiver routines and signal the closure of | ||
461 | the link */ | ||
462 | if (pops && pops->disconn_ind) { | ||
463 | pops->disconn_ind(cprot); | ||
464 | return 1; | ||
465 | } | ||
466 | break; | ||
467 | #endif /* CONFIG_ISDN_X25 */ | ||
468 | case ISDN_STAT_BCONN: | ||
469 | /* B-Channel is up */ | ||
470 | isdn_net_zero_frame_cnt(lp); | ||
471 | switch (lp->dialstate) { | ||
472 | case 5: | ||
473 | case 6: | ||
474 | case 7: | ||
475 | case 8: | ||
476 | case 9: | ||
477 | case 10: | ||
478 | case 12: | ||
479 | if (lp->dialstate <= 6) { | ||
480 | dev->usage[idx] |= ISDN_USAGE_OUTGOING; | ||
481 | isdn_info_update(); | ||
482 | } else | ||
483 | dev->rx_netdev[idx] = p; | ||
484 | lp->dialstate = 0; | ||
485 | isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 1); | ||
486 | if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK) | ||
487 | isdn_net_ciscohdlck_connected(lp); | ||
488 | if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) { | ||
489 | if (lp->master) { /* is lp a slave? */ | ||
490 | isdn_net_dev *nd = ISDN_MASTER_PRIV(lp)->netdev; | ||
491 | isdn_net_add_to_bundle(nd, lp); | ||
492 | } | ||
493 | } | ||
494 | printk(KERN_INFO "isdn_net: %s connected\n", p->dev->name); | ||
495 | /* If first Chargeinfo comes before B-Channel connect, | ||
496 | * we correct the timestamp here. | ||
497 | */ | ||
498 | lp->chargetime = jiffies; | ||
499 | |||
500 | /* reset dial-timeout */ | ||
501 | lp->dialstarted = 0; | ||
502 | lp->dialwait_timer = 0; | ||
503 | |||
504 | #ifdef CONFIG_ISDN_PPP | ||
505 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) | ||
506 | isdn_ppp_wakeup_daemon(lp); | ||
507 | #endif | ||
508 | #ifdef CONFIG_ISDN_X25 | ||
509 | /* try if there are generic concap receiver routines */ | ||
510 | if (pops) | ||
511 | if (pops->connect_ind) | ||
512 | pops->connect_ind(cprot); | ||
513 | #endif /* CONFIG_ISDN_X25 */ | ||
514 | /* ppp needs to do negotiations first */ | ||
515 | if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) | ||
516 | isdn_net_device_wake_queue(lp); | ||
517 | return 1; | ||
518 | } | ||
519 | break; | ||
520 | case ISDN_STAT_NODCH: | ||
521 | /* No D-Channel avail. */ | ||
522 | if (lp->dialstate == 4) { | ||
523 | lp->dialstate--; | ||
524 | return 1; | ||
525 | } | ||
526 | break; | ||
527 | case ISDN_STAT_CINF: | ||
528 | /* Charge-info from TelCo. Calculate interval between | ||
529 | * charge-infos and set timestamp for last info for | ||
530 | * usage by isdn_net_autohup() | ||
531 | */ | ||
532 | lp->charge++; | ||
533 | if (lp->hupflags & ISDN_HAVECHARGE) { | ||
534 | lp->hupflags &= ~ISDN_WAITCHARGE; | ||
535 | lp->chargeint = jiffies - lp->chargetime - (2 * HZ); | ||
536 | } | ||
537 | if (lp->hupflags & ISDN_WAITCHARGE) | ||
538 | lp->hupflags |= ISDN_HAVECHARGE; | ||
539 | lp->chargetime = jiffies; | ||
540 | printk(KERN_DEBUG "isdn_net: Got CINF chargetime of %s now %lu\n", | ||
541 | p->dev->name, lp->chargetime); | ||
542 | return 1; | ||
543 | } | ||
544 | } | ||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | /* | ||
549 | * Perform dialout for net-interfaces and timeout-handling for | ||
550 | * D-Channel-up and B-Channel-up Messages. | ||
551 | * This function is initially called from within isdn_net_start_xmit() or | ||
552 | * or isdn_net_find_icall() after initializing the dialstate for an | ||
553 | * interface. If further calls are needed, the function schedules itself | ||
554 | * for a timer-callback via isdn_timer_function(). | ||
555 | * The dialstate is also affected by incoming status-messages from | ||
556 | * the ISDN-Channel which are handled in isdn_net_stat_callback() above. | ||
557 | */ | ||
558 | void | ||
559 | isdn_net_dial(void) | ||
560 | { | ||
561 | isdn_net_dev *p = dev->netdev; | ||
562 | int anymore = 0; | ||
563 | int i; | ||
564 | isdn_ctrl cmd; | ||
565 | u_char *phone_number; | ||
566 | |||
567 | while (p) { | ||
568 | isdn_net_local *lp = p->local; | ||
569 | |||
570 | #ifdef ISDN_DEBUG_NET_DIAL | ||
571 | if (lp->dialstate) | ||
572 | printk(KERN_DEBUG "%s: dialstate=%d\n", p->dev->name, lp->dialstate); | ||
573 | #endif | ||
574 | switch (lp->dialstate) { | ||
575 | case 0: | ||
576 | /* Nothing to do for this interface */ | ||
577 | break; | ||
578 | case 1: | ||
579 | /* Initiate dialout. Set phone-number-pointer to first number | ||
580 | * of interface. | ||
581 | */ | ||
582 | lp->dial = lp->phone[1]; | ||
583 | if (!lp->dial) { | ||
584 | printk(KERN_WARNING "%s: phone number deleted?\n", | ||
585 | p->dev->name); | ||
586 | isdn_net_hangup(p->dev); | ||
587 | break; | ||
588 | } | ||
589 | anymore = 1; | ||
590 | |||
591 | if (lp->dialtimeout > 0) | ||
592 | if (lp->dialstarted == 0 || time_after(jiffies, lp->dialstarted + lp->dialtimeout + lp->dialwait)) { | ||
593 | lp->dialstarted = jiffies; | ||
594 | lp->dialwait_timer = 0; | ||
595 | } | ||
596 | |||
597 | lp->dialstate++; | ||
598 | /* Fall through */ | ||
599 | case 2: | ||
600 | /* Prepare dialing. Clear EAZ, then set EAZ. */ | ||
601 | cmd.driver = lp->isdn_device; | ||
602 | cmd.arg = lp->isdn_channel; | ||
603 | cmd.command = ISDN_CMD_CLREAZ; | ||
604 | isdn_command(&cmd); | ||
605 | sprintf(cmd.parm.num, "%s", isdn_map_eaz2msn(lp->msn, cmd.driver)); | ||
606 | cmd.command = ISDN_CMD_SETEAZ; | ||
607 | isdn_command(&cmd); | ||
608 | lp->dialretry = 0; | ||
609 | anymore = 1; | ||
610 | lp->dialstate++; | ||
611 | /* Fall through */ | ||
612 | case 3: | ||
613 | /* Setup interface, dial current phone-number, switch to next number. | ||
614 | * If list of phone-numbers is exhausted, increment | ||
615 | * retry-counter. | ||
616 | */ | ||
617 | if (dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF)) { | ||
618 | char *s; | ||
619 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) | ||
620 | s = "dial suppressed: isdn system stopped"; | ||
621 | else | ||
622 | s = "dial suppressed: dialmode `off'"; | ||
623 | isdn_net_unreachable(p->dev, NULL, s); | ||
624 | isdn_net_hangup(p->dev); | ||
625 | break; | ||
626 | } | ||
627 | cmd.driver = lp->isdn_device; | ||
628 | cmd.command = ISDN_CMD_SETL2; | ||
629 | cmd.arg = lp->isdn_channel + (lp->l2_proto << 8); | ||
630 | isdn_command(&cmd); | ||
631 | cmd.driver = lp->isdn_device; | ||
632 | cmd.command = ISDN_CMD_SETL3; | ||
633 | cmd.arg = lp->isdn_channel + (lp->l3_proto << 8); | ||
634 | isdn_command(&cmd); | ||
635 | cmd.driver = lp->isdn_device; | ||
636 | cmd.arg = lp->isdn_channel; | ||
637 | if (!lp->dial) { | ||
638 | printk(KERN_WARNING "%s: phone number deleted?\n", | ||
639 | p->dev->name); | ||
640 | isdn_net_hangup(p->dev); | ||
641 | break; | ||
642 | } | ||
643 | if (!strncmp(lp->dial->num, "LEASED", strlen("LEASED"))) { | ||
644 | lp->dialstate = 4; | ||
645 | printk(KERN_INFO "%s: Open leased line ...\n", p->dev->name); | ||
646 | } else { | ||
647 | if (lp->dialtimeout > 0) | ||
648 | if (time_after(jiffies, lp->dialstarted + lp->dialtimeout)) { | ||
649 | lp->dialwait_timer = jiffies + lp->dialwait; | ||
650 | lp->dialstarted = 0; | ||
651 | isdn_net_unreachable(p->dev, NULL, "dial: timed out"); | ||
652 | isdn_net_hangup(p->dev); | ||
653 | break; | ||
654 | } | ||
655 | |||
656 | cmd.driver = lp->isdn_device; | ||
657 | cmd.command = ISDN_CMD_DIAL; | ||
658 | cmd.parm.setup.si2 = 0; | ||
659 | |||
660 | /* check for DOV */ | ||
661 | phone_number = lp->dial->num; | ||
662 | if ((*phone_number == 'v') || | ||
663 | (*phone_number == 'V')) { /* DOV call */ | ||
664 | cmd.parm.setup.si1 = 1; | ||
665 | } else { /* DATA call */ | ||
666 | cmd.parm.setup.si1 = 7; | ||
667 | } | ||
668 | |||
669 | strcpy(cmd.parm.setup.phone, phone_number); | ||
670 | /* | ||
671 | * Switch to next number or back to start if at end of list. | ||
672 | */ | ||
673 | if (!(lp->dial = (isdn_net_phone *) lp->dial->next)) { | ||
674 | lp->dial = lp->phone[1]; | ||
675 | lp->dialretry++; | ||
676 | |||
677 | if (lp->dialretry > lp->dialmax) { | ||
678 | if (lp->dialtimeout == 0) { | ||
679 | lp->dialwait_timer = jiffies + lp->dialwait; | ||
680 | lp->dialstarted = 0; | ||
681 | isdn_net_unreachable(p->dev, NULL, "dial: tried all numbers dialmax times"); | ||
682 | } | ||
683 | isdn_net_hangup(p->dev); | ||
684 | break; | ||
685 | } | ||
686 | } | ||
687 | sprintf(cmd.parm.setup.eazmsn, "%s", | ||
688 | isdn_map_eaz2msn(lp->msn, cmd.driver)); | ||
689 | i = isdn_dc2minor(lp->isdn_device, lp->isdn_channel); | ||
690 | if (i >= 0) { | ||
691 | strcpy(dev->num[i], cmd.parm.setup.phone); | ||
692 | dev->usage[i] |= ISDN_USAGE_OUTGOING; | ||
693 | isdn_info_update(); | ||
694 | } | ||
695 | printk(KERN_INFO "%s: dialing %d %s... %s\n", p->dev->name, | ||
696 | lp->dialretry, cmd.parm.setup.phone, | ||
697 | (cmd.parm.setup.si1 == 1) ? "DOV" : ""); | ||
698 | lp->dtimer = 0; | ||
699 | #ifdef ISDN_DEBUG_NET_DIAL | ||
700 | printk(KERN_DEBUG "dial: d=%d c=%d\n", lp->isdn_device, | ||
701 | lp->isdn_channel); | ||
702 | #endif | ||
703 | isdn_command(&cmd); | ||
704 | } | ||
705 | lp->huptimer = 0; | ||
706 | lp->outgoing = 1; | ||
707 | if (lp->chargeint) { | ||
708 | lp->hupflags |= ISDN_HAVECHARGE; | ||
709 | lp->hupflags &= ~ISDN_WAITCHARGE; | ||
710 | } else { | ||
711 | lp->hupflags |= ISDN_WAITCHARGE; | ||
712 | lp->hupflags &= ~ISDN_HAVECHARGE; | ||
713 | } | ||
714 | anymore = 1; | ||
715 | lp->dialstate = | ||
716 | (lp->cbdelay && | ||
717 | (lp->flags & ISDN_NET_CBOUT)) ? 12 : 4; | ||
718 | break; | ||
719 | case 4: | ||
720 | /* Wait for D-Channel-connect. | ||
721 | * If timeout, switch back to state 3. | ||
722 | * Dialmax-handling moved to state 3. | ||
723 | */ | ||
724 | if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10) | ||
725 | lp->dialstate = 3; | ||
726 | anymore = 1; | ||
727 | break; | ||
728 | case 5: | ||
729 | /* Got D-Channel-Connect, send B-Channel-request */ | ||
730 | cmd.driver = lp->isdn_device; | ||
731 | cmd.arg = lp->isdn_channel; | ||
732 | cmd.command = ISDN_CMD_ACCEPTB; | ||
733 | anymore = 1; | ||
734 | lp->dtimer = 0; | ||
735 | lp->dialstate++; | ||
736 | isdn_command(&cmd); | ||
737 | break; | ||
738 | case 6: | ||
739 | /* Wait for B- or D-Channel-connect. If timeout, | ||
740 | * switch back to state 3. | ||
741 | */ | ||
742 | #ifdef ISDN_DEBUG_NET_DIAL | ||
743 | printk(KERN_DEBUG "dialtimer2: %d\n", lp->dtimer); | ||
744 | #endif | ||
745 | if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10) | ||
746 | lp->dialstate = 3; | ||
747 | anymore = 1; | ||
748 | break; | ||
749 | case 7: | ||
750 | /* Got incoming Call, setup L2 and L3 protocols, | ||
751 | * then wait for D-Channel-connect | ||
752 | */ | ||
753 | #ifdef ISDN_DEBUG_NET_DIAL | ||
754 | printk(KERN_DEBUG "dialtimer4: %d\n", lp->dtimer); | ||
755 | #endif | ||
756 | cmd.driver = lp->isdn_device; | ||
757 | cmd.command = ISDN_CMD_SETL2; | ||
758 | cmd.arg = lp->isdn_channel + (lp->l2_proto << 8); | ||
759 | isdn_command(&cmd); | ||
760 | cmd.driver = lp->isdn_device; | ||
761 | cmd.command = ISDN_CMD_SETL3; | ||
762 | cmd.arg = lp->isdn_channel + (lp->l3_proto << 8); | ||
763 | isdn_command(&cmd); | ||
764 | if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT15) | ||
765 | isdn_net_hangup(p->dev); | ||
766 | else { | ||
767 | anymore = 1; | ||
768 | lp->dialstate++; | ||
769 | } | ||
770 | break; | ||
771 | case 9: | ||
772 | /* Got incoming D-Channel-Connect, send B-Channel-request */ | ||
773 | cmd.driver = lp->isdn_device; | ||
774 | cmd.arg = lp->isdn_channel; | ||
775 | cmd.command = ISDN_CMD_ACCEPTB; | ||
776 | isdn_command(&cmd); | ||
777 | anymore = 1; | ||
778 | lp->dtimer = 0; | ||
779 | lp->dialstate++; | ||
780 | break; | ||
781 | case 8: | ||
782 | case 10: | ||
783 | /* Wait for B- or D-channel-connect */ | ||
784 | #ifdef ISDN_DEBUG_NET_DIAL | ||
785 | printk(KERN_DEBUG "dialtimer4: %d\n", lp->dtimer); | ||
786 | #endif | ||
787 | if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10) | ||
788 | isdn_net_hangup(p->dev); | ||
789 | else | ||
790 | anymore = 1; | ||
791 | break; | ||
792 | case 11: | ||
793 | /* Callback Delay */ | ||
794 | if (lp->dtimer++ > lp->cbdelay) | ||
795 | lp->dialstate = 1; | ||
796 | anymore = 1; | ||
797 | break; | ||
798 | case 12: | ||
799 | /* Remote does callback. Hangup after cbdelay, then wait for incoming | ||
800 | * call (in state 4). | ||
801 | */ | ||
802 | if (lp->dtimer++ > lp->cbdelay) | ||
803 | { | ||
804 | printk(KERN_INFO "%s: hangup waiting for callback ...\n", p->dev->name); | ||
805 | lp->dtimer = 0; | ||
806 | lp->dialstate = 4; | ||
807 | cmd.driver = lp->isdn_device; | ||
808 | cmd.command = ISDN_CMD_HANGUP; | ||
809 | cmd.arg = lp->isdn_channel; | ||
810 | isdn_command(&cmd); | ||
811 | isdn_all_eaz(lp->isdn_device, lp->isdn_channel); | ||
812 | } | ||
813 | anymore = 1; | ||
814 | break; | ||
815 | default: | ||
816 | printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n", | ||
817 | lp->dialstate, p->dev->name); | ||
818 | } | ||
819 | p = (isdn_net_dev *) p->next; | ||
820 | } | ||
821 | isdn_timer_ctrl(ISDN_TIMER_NETDIAL, anymore); | ||
822 | } | ||
823 | |||
824 | /* | ||
825 | * Perform hangup for a net-interface. | ||
826 | */ | ||
827 | void | ||
828 | isdn_net_hangup(struct net_device *d) | ||
829 | { | ||
830 | isdn_net_local *lp = netdev_priv(d); | ||
831 | isdn_ctrl cmd; | ||
832 | #ifdef CONFIG_ISDN_X25 | ||
833 | struct concap_proto *cprot = lp->netdev->cprot; | ||
834 | struct concap_proto_ops *pops = cprot ? cprot->pops : NULL; | ||
835 | #endif | ||
836 | |||
837 | if (lp->flags & ISDN_NET_CONNECTED) { | ||
838 | if (lp->slave != NULL) { | ||
839 | isdn_net_local *slp = ISDN_SLAVE_PRIV(lp); | ||
840 | if (slp->flags & ISDN_NET_CONNECTED) { | ||
841 | printk(KERN_INFO | ||
842 | "isdn_net: hang up slave %s before %s\n", | ||
843 | lp->slave->name, d->name); | ||
844 | isdn_net_hangup(lp->slave); | ||
845 | } | ||
846 | } | ||
847 | printk(KERN_INFO "isdn_net: local hangup %s\n", d->name); | ||
848 | #ifdef CONFIG_ISDN_PPP | ||
849 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) | ||
850 | isdn_ppp_free(lp); | ||
851 | #endif | ||
852 | isdn_net_lp_disconnected(lp); | ||
853 | #ifdef CONFIG_ISDN_X25 | ||
854 | /* try if there are generic encap protocol | ||
855 | receiver routines and signal the closure of | ||
856 | the link */ | ||
857 | if (pops && pops->disconn_ind) | ||
858 | pops->disconn_ind(cprot); | ||
859 | #endif /* CONFIG_ISDN_X25 */ | ||
860 | |||
861 | cmd.driver = lp->isdn_device; | ||
862 | cmd.command = ISDN_CMD_HANGUP; | ||
863 | cmd.arg = lp->isdn_channel; | ||
864 | isdn_command(&cmd); | ||
865 | printk(KERN_INFO "%s: Chargesum is %d\n", d->name, lp->charge); | ||
866 | isdn_all_eaz(lp->isdn_device, lp->isdn_channel); | ||
867 | } | ||
868 | isdn_net_unbind_channel(lp); | ||
869 | } | ||
870 | |||
871 | typedef struct { | ||
872 | __be16 source; | ||
873 | __be16 dest; | ||
874 | } ip_ports; | ||
875 | |||
876 | static void | ||
877 | isdn_net_log_skb(struct sk_buff *skb, isdn_net_local *lp) | ||
878 | { | ||
879 | /* hopefully, this was set correctly */ | ||
880 | const u_char *p = skb_network_header(skb); | ||
881 | unsigned short proto = ntohs(skb->protocol); | ||
882 | int data_ofs; | ||
883 | ip_ports *ipp; | ||
884 | char addinfo[100]; | ||
885 | |||
886 | addinfo[0] = '\0'; | ||
887 | /* This check stolen from 2.1.72 dev_queue_xmit_nit() */ | ||
888 | if (p < skb->data || skb_network_header(skb) >= skb_tail_pointer(skb)) { | ||
889 | /* fall back to old isdn_net_log_packet method() */ | ||
890 | char *buf = skb->data; | ||
891 | |||
892 | printk(KERN_DEBUG "isdn_net: protocol %04x is buggy, dev %s\n", skb->protocol, lp->netdev->dev->name); | ||
893 | p = buf; | ||
894 | proto = ETH_P_IP; | ||
895 | switch (lp->p_encap) { | ||
896 | case ISDN_NET_ENCAP_IPTYP: | ||
897 | proto = ntohs(*(__be16 *)&buf[0]); | ||
898 | p = &buf[2]; | ||
899 | break; | ||
900 | case ISDN_NET_ENCAP_ETHER: | ||
901 | proto = ntohs(*(__be16 *)&buf[12]); | ||
902 | p = &buf[14]; | ||
903 | break; | ||
904 | case ISDN_NET_ENCAP_CISCOHDLC: | ||
905 | proto = ntohs(*(__be16 *)&buf[2]); | ||
906 | p = &buf[4]; | ||
907 | break; | ||
908 | #ifdef CONFIG_ISDN_PPP | ||
909 | case ISDN_NET_ENCAP_SYNCPPP: | ||
910 | proto = ntohs(skb->protocol); | ||
911 | p = &buf[IPPP_MAX_HEADER]; | ||
912 | break; | ||
913 | #endif | ||
914 | } | ||
915 | } | ||
916 | data_ofs = ((p[0] & 15) * 4); | ||
917 | switch (proto) { | ||
918 | case ETH_P_IP: | ||
919 | switch (p[9]) { | ||
920 | case 1: | ||
921 | strcpy(addinfo, " ICMP"); | ||
922 | break; | ||
923 | case 2: | ||
924 | strcpy(addinfo, " IGMP"); | ||
925 | break; | ||
926 | case 4: | ||
927 | strcpy(addinfo, " IPIP"); | ||
928 | break; | ||
929 | case 6: | ||
930 | ipp = (ip_ports *) (&p[data_ofs]); | ||
931 | sprintf(addinfo, " TCP, port: %d -> %d", ntohs(ipp->source), | ||
932 | ntohs(ipp->dest)); | ||
933 | break; | ||
934 | case 8: | ||
935 | strcpy(addinfo, " EGP"); | ||
936 | break; | ||
937 | case 12: | ||
938 | strcpy(addinfo, " PUP"); | ||
939 | break; | ||
940 | case 17: | ||
941 | ipp = (ip_ports *) (&p[data_ofs]); | ||
942 | sprintf(addinfo, " UDP, port: %d -> %d", ntohs(ipp->source), | ||
943 | ntohs(ipp->dest)); | ||
944 | break; | ||
945 | case 22: | ||
946 | strcpy(addinfo, " IDP"); | ||
947 | break; | ||
948 | } | ||
949 | printk(KERN_INFO "OPEN: %pI4 -> %pI4%s\n", | ||
950 | p + 12, p + 16, addinfo); | ||
951 | break; | ||
952 | case ETH_P_ARP: | ||
953 | printk(KERN_INFO "OPEN: ARP %pI4 -> *.*.*.* ?%pI4\n", | ||
954 | p + 14, p + 24); | ||
955 | break; | ||
956 | } | ||
957 | } | ||
958 | |||
959 | /* | ||
960 | * this function is used to send supervisory data, i.e. data which was | ||
961 | * not received from the network layer, but e.g. frames from ipppd, CCP | ||
962 | * reset frames etc. | ||
963 | */ | ||
964 | void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb) | ||
965 | { | ||
966 | if (in_irq()) { | ||
967 | // we can't grab the lock from irq context, | ||
968 | // so we just queue the packet | ||
969 | skb_queue_tail(&lp->super_tx_queue, skb); | ||
970 | schedule_work(&lp->tqueue); | ||
971 | return; | ||
972 | } | ||
973 | |||
974 | spin_lock_bh(&lp->xmit_lock); | ||
975 | if (!isdn_net_lp_busy(lp)) { | ||
976 | isdn_net_writebuf_skb(lp, skb); | ||
977 | } else { | ||
978 | skb_queue_tail(&lp->super_tx_queue, skb); | ||
979 | } | ||
980 | spin_unlock_bh(&lp->xmit_lock); | ||
981 | } | ||
982 | |||
983 | /* | ||
984 | * called from tq_immediate | ||
985 | */ | ||
986 | static void isdn_net_softint(struct work_struct *work) | ||
987 | { | ||
988 | isdn_net_local *lp = container_of(work, isdn_net_local, tqueue); | ||
989 | struct sk_buff *skb; | ||
990 | |||
991 | spin_lock_bh(&lp->xmit_lock); | ||
992 | while (!isdn_net_lp_busy(lp)) { | ||
993 | skb = skb_dequeue(&lp->super_tx_queue); | ||
994 | if (!skb) | ||
995 | break; | ||
996 | isdn_net_writebuf_skb(lp, skb); | ||
997 | } | ||
998 | spin_unlock_bh(&lp->xmit_lock); | ||
999 | } | ||
1000 | |||
1001 | /* | ||
1002 | * all frames sent from the (net) LL to a HL driver should go via this function | ||
1003 | * it's serialized by the caller holding the lp->xmit_lock spinlock | ||
1004 | */ | ||
1005 | void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb) | ||
1006 | { | ||
1007 | int ret; | ||
1008 | int len = skb->len; /* save len */ | ||
1009 | |||
1010 | /* before obtaining the lock the caller should have checked that | ||
1011 | the lp isn't busy */ | ||
1012 | if (isdn_net_lp_busy(lp)) { | ||
1013 | printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__); | ||
1014 | goto error; | ||
1015 | } | ||
1016 | |||
1017 | if (!(lp->flags & ISDN_NET_CONNECTED)) { | ||
1018 | printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__); | ||
1019 | goto error; | ||
1020 | } | ||
1021 | ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb); | ||
1022 | if (ret != len) { | ||
1023 | /* we should never get here */ | ||
1024 | printk(KERN_WARNING "%s: HL driver queue full\n", lp->netdev->dev->name); | ||
1025 | goto error; | ||
1026 | } | ||
1027 | |||
1028 | lp->transcount += len; | ||
1029 | isdn_net_inc_frame_cnt(lp); | ||
1030 | return; | ||
1031 | |||
1032 | error: | ||
1033 | dev_kfree_skb(skb); | ||
1034 | lp->stats.tx_errors++; | ||
1035 | |||
1036 | } | ||
1037 | |||
1038 | |||
1039 | /* | ||
1040 | * Helper function for isdn_net_start_xmit. | ||
1041 | * When called, the connection is already established. | ||
1042 | * Based on cps-calculation, check if device is overloaded. | ||
1043 | * If so, and if a slave exists, trigger dialing for it. | ||
1044 | * If any slave is online, deliver packets using a simple round robin | ||
1045 | * scheme. | ||
1046 | * | ||
1047 | * Return: 0 on success, !0 on failure. | ||
1048 | */ | ||
1049 | |||
1050 | static int | ||
1051 | isdn_net_xmit(struct net_device *ndev, struct sk_buff *skb) | ||
1052 | { | ||
1053 | isdn_net_dev *nd; | ||
1054 | isdn_net_local *slp; | ||
1055 | isdn_net_local *lp = netdev_priv(ndev); | ||
1056 | int retv = NETDEV_TX_OK; | ||
1057 | |||
1058 | if (((isdn_net_local *) netdev_priv(ndev))->master) { | ||
1059 | printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__); | ||
1060 | dev_kfree_skb(skb); | ||
1061 | return NETDEV_TX_OK; | ||
1062 | } | ||
1063 | |||
1064 | /* For the other encaps the header has already been built */ | ||
1065 | #ifdef CONFIG_ISDN_PPP | ||
1066 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) { | ||
1067 | return isdn_ppp_xmit(skb, ndev); | ||
1068 | } | ||
1069 | #endif | ||
1070 | nd = ((isdn_net_local *) netdev_priv(ndev))->netdev; | ||
1071 | lp = isdn_net_get_locked_lp(nd); | ||
1072 | if (!lp) { | ||
1073 | printk(KERN_WARNING "%s: all channels busy - requeuing!\n", ndev->name); | ||
1074 | return NETDEV_TX_BUSY; | ||
1075 | } | ||
1076 | /* we have our lp locked from now on */ | ||
1077 | |||
1078 | /* Reset hangup-timeout */ | ||
1079 | lp->huptimer = 0; // FIXME? | ||
1080 | isdn_net_writebuf_skb(lp, skb); | ||
1081 | spin_unlock_bh(&lp->xmit_lock); | ||
1082 | |||
1083 | /* the following stuff is here for backwards compatibility. | ||
1084 | * in future, start-up and hangup of slaves (based on current load) | ||
1085 | * should move to userspace and get based on an overall cps | ||
1086 | * calculation | ||
1087 | */ | ||
1088 | if (lp->cps > lp->triggercps) { | ||
1089 | if (lp->slave) { | ||
1090 | if (!lp->sqfull) { | ||
1091 | /* First time overload: set timestamp only */ | ||
1092 | lp->sqfull = 1; | ||
1093 | lp->sqfull_stamp = jiffies; | ||
1094 | } else { | ||
1095 | /* subsequent overload: if slavedelay exceeded, start dialing */ | ||
1096 | if (time_after(jiffies, lp->sqfull_stamp + lp->slavedelay)) { | ||
1097 | slp = ISDN_SLAVE_PRIV(lp); | ||
1098 | if (!(slp->flags & ISDN_NET_CONNECTED)) { | ||
1099 | isdn_net_force_dial_lp(ISDN_SLAVE_PRIV(lp)); | ||
1100 | } | ||
1101 | } | ||
1102 | } | ||
1103 | } | ||
1104 | } else { | ||
1105 | if (lp->sqfull && time_after(jiffies, lp->sqfull_stamp + lp->slavedelay + (10 * HZ))) { | ||
1106 | lp->sqfull = 0; | ||
1107 | } | ||
1108 | /* this is a hack to allow auto-hangup for slaves on moderate loads */ | ||
1109 | nd->queue = nd->local; | ||
1110 | } | ||
1111 | |||
1112 | return retv; | ||
1113 | |||
1114 | } | ||
1115 | |||
1116 | static void | ||
1117 | isdn_net_adjust_hdr(struct sk_buff *skb, struct net_device *dev) | ||
1118 | { | ||
1119 | isdn_net_local *lp = netdev_priv(dev); | ||
1120 | if (!skb) | ||
1121 | return; | ||
1122 | if (lp->p_encap == ISDN_NET_ENCAP_ETHER) { | ||
1123 | const int pullsize = skb_network_offset(skb) - ETH_HLEN; | ||
1124 | if (pullsize > 0) { | ||
1125 | printk(KERN_DEBUG "isdn_net: Pull junk %d\n", pullsize); | ||
1126 | skb_pull(skb, pullsize); | ||
1127 | } | ||
1128 | } | ||
1129 | } | ||
1130 | |||
1131 | |||
1132 | static void isdn_net_tx_timeout(struct net_device *ndev) | ||
1133 | { | ||
1134 | isdn_net_local *lp = netdev_priv(ndev); | ||
1135 | |||
1136 | printk(KERN_WARNING "isdn_tx_timeout dev %s dialstate %d\n", ndev->name, lp->dialstate); | ||
1137 | if (!lp->dialstate) { | ||
1138 | lp->stats.tx_errors++; | ||
1139 | /* | ||
1140 | * There is a certain probability that this currently | ||
1141 | * works at all because if we always wake up the interface, | ||
1142 | * then upper layer will try to send the next packet | ||
1143 | * immediately. And then, the old clean_up logic in the | ||
1144 | * driver will hopefully continue to work as it used to do. | ||
1145 | * | ||
1146 | * This is rather primitive right know, we better should | ||
1147 | * clean internal queues here, in particular for multilink and | ||
1148 | * ppp, and reset HL driver's channel, too. --HE | ||
1149 | * | ||
1150 | * actually, this may not matter at all, because ISDN hardware | ||
1151 | * should not see transmitter hangs at all IMO | ||
1152 | * changed KERN_DEBUG to KERN_WARNING to find out if this is | ||
1153 | * ever called --KG | ||
1154 | */ | ||
1155 | } | ||
1156 | netif_trans_update(ndev); | ||
1157 | netif_wake_queue(ndev); | ||
1158 | } | ||
1159 | |||
1160 | /* | ||
1161 | * Try sending a packet. | ||
1162 | * If this interface isn't connected to a ISDN-Channel, find a free channel, | ||
1163 | * and start dialing. | ||
1164 | */ | ||
1165 | static netdev_tx_t | ||
1166 | isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev) | ||
1167 | { | ||
1168 | isdn_net_local *lp = netdev_priv(ndev); | ||
1169 | #ifdef CONFIG_ISDN_X25 | ||
1170 | struct concap_proto *cprot = lp->netdev->cprot; | ||
1171 | /* At this point hard_start_xmit() passes control to the encapsulation | ||
1172 | protocol (if present). | ||
1173 | For X.25 auto-dialing is completly bypassed because: | ||
1174 | - It does not conform with the semantics of a reliable datalink | ||
1175 | service as needed by X.25 PLP. | ||
1176 | - I don't want that the interface starts dialing when the network layer | ||
1177 | sends a message which requests to disconnect the lapb link (or if it | ||
1178 | sends any other message not resulting in data transmission). | ||
1179 | Instead, dialing will be initiated by the encapsulation protocol entity | ||
1180 | when a dl_establish request is received from the upper layer. | ||
1181 | */ | ||
1182 | if (cprot && cprot->pops) { | ||
1183 | int ret = cprot->pops->encap_and_xmit(cprot, skb); | ||
1184 | |||
1185 | if (ret) | ||
1186 | netif_stop_queue(ndev); | ||
1187 | return ret; | ||
1188 | } else | ||
1189 | #endif | ||
1190 | /* auto-dialing xmit function */ | ||
1191 | { | ||
1192 | #ifdef ISDN_DEBUG_NET_DUMP | ||
1193 | u_char *buf; | ||
1194 | #endif | ||
1195 | isdn_net_adjust_hdr(skb, ndev); | ||
1196 | #ifdef ISDN_DEBUG_NET_DUMP | ||
1197 | buf = skb->data; | ||
1198 | isdn_dumppkt("S:", buf, skb->len, 40); | ||
1199 | #endif | ||
1200 | |||
1201 | if (!(lp->flags & ISDN_NET_CONNECTED)) { | ||
1202 | int chi; | ||
1203 | /* only do autodial if allowed by config */ | ||
1204 | if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) { | ||
1205 | isdn_net_unreachable(ndev, skb, "dial rejected: interface not in dialmode `auto'"); | ||
1206 | dev_kfree_skb(skb); | ||
1207 | return NETDEV_TX_OK; | ||
1208 | } | ||
1209 | if (lp->phone[1]) { | ||
1210 | ulong flags; | ||
1211 | |||
1212 | if (lp->dialwait_timer <= 0) | ||
1213 | if (lp->dialstarted > 0 && lp->dialtimeout > 0 && time_before(jiffies, lp->dialstarted + lp->dialtimeout + lp->dialwait)) | ||
1214 | lp->dialwait_timer = lp->dialstarted + lp->dialtimeout + lp->dialwait; | ||
1215 | |||
1216 | if (lp->dialwait_timer > 0) { | ||
1217 | if (time_before(jiffies, lp->dialwait_timer)) { | ||
1218 | isdn_net_unreachable(ndev, skb, "dial rejected: retry-time not reached"); | ||
1219 | dev_kfree_skb(skb); | ||
1220 | return NETDEV_TX_OK; | ||
1221 | } else | ||
1222 | lp->dialwait_timer = 0; | ||
1223 | } | ||
1224 | /* Grab a free ISDN-Channel */ | ||
1225 | spin_lock_irqsave(&dev->lock, flags); | ||
1226 | if (((chi = | ||
1227 | isdn_get_free_channel( | ||
1228 | ISDN_USAGE_NET, | ||
1229 | lp->l2_proto, | ||
1230 | lp->l3_proto, | ||
1231 | lp->pre_device, | ||
1232 | lp->pre_channel, | ||
1233 | lp->msn) | ||
1234 | ) < 0) && | ||
1235 | ((chi = | ||
1236 | isdn_get_free_channel( | ||
1237 | ISDN_USAGE_NET, | ||
1238 | lp->l2_proto, | ||
1239 | lp->l3_proto, | ||
1240 | lp->pre_device, | ||
1241 | lp->pre_channel^1, | ||
1242 | lp->msn) | ||
1243 | ) < 0)) { | ||
1244 | spin_unlock_irqrestore(&dev->lock, flags); | ||
1245 | isdn_net_unreachable(ndev, skb, | ||
1246 | "No channel"); | ||
1247 | dev_kfree_skb(skb); | ||
1248 | return NETDEV_TX_OK; | ||
1249 | } | ||
1250 | /* Log packet, which triggered dialing */ | ||
1251 | if (dev->net_verbose) | ||
1252 | isdn_net_log_skb(skb, lp); | ||
1253 | lp->dialstate = 1; | ||
1254 | /* Connect interface with channel */ | ||
1255 | isdn_net_bind_channel(lp, chi); | ||
1256 | #ifdef CONFIG_ISDN_PPP | ||
1257 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) { | ||
1258 | /* no 'first_skb' handling for syncPPP */ | ||
1259 | if (isdn_ppp_bind(lp) < 0) { | ||
1260 | dev_kfree_skb(skb); | ||
1261 | isdn_net_unbind_channel(lp); | ||
1262 | spin_unlock_irqrestore(&dev->lock, flags); | ||
1263 | return NETDEV_TX_OK; /* STN (skb to nirvana) ;) */ | ||
1264 | } | ||
1265 | #ifdef CONFIG_IPPP_FILTER | ||
1266 | if (isdn_ppp_autodial_filter(skb, lp)) { | ||
1267 | isdn_ppp_free(lp); | ||
1268 | isdn_net_unbind_channel(lp); | ||
1269 | spin_unlock_irqrestore(&dev->lock, flags); | ||
1270 | isdn_net_unreachable(ndev, skb, "dial rejected: packet filtered"); | ||
1271 | dev_kfree_skb(skb); | ||
1272 | return NETDEV_TX_OK; | ||
1273 | } | ||
1274 | #endif | ||
1275 | spin_unlock_irqrestore(&dev->lock, flags); | ||
1276 | isdn_net_dial(); /* Initiate dialing */ | ||
1277 | netif_stop_queue(ndev); | ||
1278 | return NETDEV_TX_BUSY; /* let upper layer requeue skb packet */ | ||
1279 | } | ||
1280 | #endif | ||
1281 | /* Initiate dialing */ | ||
1282 | spin_unlock_irqrestore(&dev->lock, flags); | ||
1283 | isdn_net_dial(); | ||
1284 | isdn_net_device_stop_queue(lp); | ||
1285 | return NETDEV_TX_BUSY; | ||
1286 | } else { | ||
1287 | isdn_net_unreachable(ndev, skb, | ||
1288 | "No phone number"); | ||
1289 | dev_kfree_skb(skb); | ||
1290 | return NETDEV_TX_OK; | ||
1291 | } | ||
1292 | } else { | ||
1293 | /* Device is connected to an ISDN channel */ | ||
1294 | netif_trans_update(ndev); | ||
1295 | if (!lp->dialstate) { | ||
1296 | /* ISDN connection is established, try sending */ | ||
1297 | int ret; | ||
1298 | ret = (isdn_net_xmit(ndev, skb)); | ||
1299 | if (ret) netif_stop_queue(ndev); | ||
1300 | return ret; | ||
1301 | } else | ||
1302 | netif_stop_queue(ndev); | ||
1303 | } | ||
1304 | } | ||
1305 | return NETDEV_TX_BUSY; | ||
1306 | } | ||
1307 | |||
1308 | /* | ||
1309 | * Shutdown a net-interface. | ||
1310 | */ | ||
1311 | static int | ||
1312 | isdn_net_close(struct net_device *dev) | ||
1313 | { | ||
1314 | struct net_device *p; | ||
1315 | #ifdef CONFIG_ISDN_X25 | ||
1316 | struct concap_proto *cprot = | ||
1317 | ((isdn_net_local *)netdev_priv(dev))->netdev->cprot; | ||
1318 | /* printk(KERN_DEBUG "isdn_net_close %s\n" , dev-> name); */ | ||
1319 | #endif | ||
1320 | |||
1321 | #ifdef CONFIG_ISDN_X25 | ||
1322 | if (cprot && cprot->pops) cprot->pops->close(cprot); | ||
1323 | #endif | ||
1324 | netif_stop_queue(dev); | ||
1325 | p = MASTER_TO_SLAVE(dev); | ||
1326 | if (p) { | ||
1327 | /* If this interface has slaves, stop them also */ | ||
1328 | while (p) { | ||
1329 | #ifdef CONFIG_ISDN_X25 | ||
1330 | cprot = ((isdn_net_local *)netdev_priv(p)) | ||
1331 | ->netdev->cprot; | ||
1332 | if (cprot && cprot->pops) | ||
1333 | cprot->pops->close(cprot); | ||
1334 | #endif | ||
1335 | isdn_net_hangup(p); | ||
1336 | p = MASTER_TO_SLAVE(p); | ||
1337 | } | ||
1338 | } | ||
1339 | isdn_net_hangup(dev); | ||
1340 | isdn_unlock_drivers(); | ||
1341 | return 0; | ||
1342 | } | ||
1343 | |||
1344 | /* | ||
1345 | * Get statistics | ||
1346 | */ | ||
1347 | static struct net_device_stats * | ||
1348 | isdn_net_get_stats(struct net_device *dev) | ||
1349 | { | ||
1350 | isdn_net_local *lp = netdev_priv(dev); | ||
1351 | return &lp->stats; | ||
1352 | } | ||
1353 | |||
1354 | /* This is simply a copy from std. eth.c EXCEPT we pull ETH_HLEN | ||
1355 | * instead of dev->hard_header_len off. This is done because the | ||
1356 | * lowlevel-driver has already pulled off its stuff when we get | ||
1357 | * here and this routine only gets called with p_encap == ETHER. | ||
1358 | * Determine the packet's protocol ID. The rule here is that we | ||
1359 | * assume 802.3 if the type field is short enough to be a length. | ||
1360 | * This is normal practice and works for any 'now in use' protocol. | ||
1361 | */ | ||
1362 | |||
1363 | static __be16 | ||
1364 | isdn_net_type_trans(struct sk_buff *skb, struct net_device *dev) | ||
1365 | { | ||
1366 | struct ethhdr *eth; | ||
1367 | unsigned char *rawp; | ||
1368 | |||
1369 | skb_reset_mac_header(skb); | ||
1370 | skb_pull(skb, ETH_HLEN); | ||
1371 | eth = eth_hdr(skb); | ||
1372 | |||
1373 | if (*eth->h_dest & 1) { | ||
1374 | if (ether_addr_equal(eth->h_dest, dev->broadcast)) | ||
1375 | skb->pkt_type = PACKET_BROADCAST; | ||
1376 | else | ||
1377 | skb->pkt_type = PACKET_MULTICAST; | ||
1378 | } | ||
1379 | /* | ||
1380 | * This ALLMULTI check should be redundant by 1.4 | ||
1381 | * so don't forget to remove it. | ||
1382 | */ | ||
1383 | |||
1384 | else if (dev->flags & (IFF_PROMISC /*| IFF_ALLMULTI*/)) { | ||
1385 | if (!ether_addr_equal(eth->h_dest, dev->dev_addr)) | ||
1386 | skb->pkt_type = PACKET_OTHERHOST; | ||
1387 | } | ||
1388 | if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) | ||
1389 | return eth->h_proto; | ||
1390 | |||
1391 | rawp = skb->data; | ||
1392 | |||
1393 | /* | ||
1394 | * This is a magic hack to spot IPX packets. Older Novell breaks | ||
1395 | * the protocol design and runs IPX over 802.3 without an 802.2 LLC | ||
1396 | * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This | ||
1397 | * won't work for fault tolerant netware but does for the rest. | ||
1398 | */ | ||
1399 | if (*(unsigned short *) rawp == 0xFFFF) | ||
1400 | return htons(ETH_P_802_3); | ||
1401 | /* | ||
1402 | * Real 802.2 LLC | ||
1403 | */ | ||
1404 | return htons(ETH_P_802_2); | ||
1405 | } | ||
1406 | |||
1407 | |||
1408 | /* | ||
1409 | * CISCO HDLC keepalive specific stuff | ||
1410 | */ | ||
1411 | static struct sk_buff* | ||
1412 | isdn_net_ciscohdlck_alloc_skb(isdn_net_local *lp, int len) | ||
1413 | { | ||
1414 | unsigned short hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; | ||
1415 | struct sk_buff *skb; | ||
1416 | |||
1417 | skb = alloc_skb(hl + len, GFP_ATOMIC); | ||
1418 | if (skb) | ||
1419 | skb_reserve(skb, hl); | ||
1420 | else | ||
1421 | printk("isdn out of mem at %s:%d!\n", __FILE__, __LINE__); | ||
1422 | return skb; | ||
1423 | } | ||
1424 | |||
1425 | /* cisco hdlck device private ioctls */ | ||
1426 | static int | ||
1427 | isdn_ciscohdlck_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | ||
1428 | { | ||
1429 | isdn_net_local *lp = netdev_priv(dev); | ||
1430 | unsigned long len = 0; | ||
1431 | unsigned long expires = 0; | ||
1432 | int tmp = 0; | ||
1433 | int period = lp->cisco_keepalive_period; | ||
1434 | s8 debserint = lp->cisco_debserint; | ||
1435 | int rc = 0; | ||
1436 | |||
1437 | if (lp->p_encap != ISDN_NET_ENCAP_CISCOHDLCK) | ||
1438 | return -EINVAL; | ||
1439 | |||
1440 | switch (cmd) { | ||
1441 | /* get/set keepalive period */ | ||
1442 | case SIOCGKEEPPERIOD: | ||
1443 | len = (unsigned long)sizeof(lp->cisco_keepalive_period); | ||
1444 | if (copy_to_user(ifr->ifr_data, | ||
1445 | &lp->cisco_keepalive_period, len)) | ||
1446 | rc = -EFAULT; | ||
1447 | break; | ||
1448 | case SIOCSKEEPPERIOD: | ||
1449 | tmp = lp->cisco_keepalive_period; | ||
1450 | len = (unsigned long)sizeof(lp->cisco_keepalive_period); | ||
1451 | if (copy_from_user(&period, ifr->ifr_data, len)) | ||
1452 | rc = -EFAULT; | ||
1453 | if ((period > 0) && (period <= 32767)) | ||
1454 | lp->cisco_keepalive_period = period; | ||
1455 | else | ||
1456 | rc = -EINVAL; | ||
1457 | if (!rc && (tmp != lp->cisco_keepalive_period)) { | ||
1458 | expires = (unsigned long)(jiffies + | ||
1459 | lp->cisco_keepalive_period * HZ); | ||
1460 | mod_timer(&lp->cisco_timer, expires); | ||
1461 | printk(KERN_INFO "%s: Keepalive period set " | ||
1462 | "to %d seconds.\n", | ||
1463 | dev->name, lp->cisco_keepalive_period); | ||
1464 | } | ||
1465 | break; | ||
1466 | |||
1467 | /* get/set debugging */ | ||
1468 | case SIOCGDEBSERINT: | ||
1469 | len = (unsigned long)sizeof(lp->cisco_debserint); | ||
1470 | if (copy_to_user(ifr->ifr_data, | ||
1471 | &lp->cisco_debserint, len)) | ||
1472 | rc = -EFAULT; | ||
1473 | break; | ||
1474 | case SIOCSDEBSERINT: | ||
1475 | len = (unsigned long)sizeof(lp->cisco_debserint); | ||
1476 | if (copy_from_user(&debserint, | ||
1477 | ifr->ifr_data, len)) | ||
1478 | rc = -EFAULT; | ||
1479 | if ((debserint >= 0) && (debserint <= 64)) | ||
1480 | lp->cisco_debserint = debserint; | ||
1481 | else | ||
1482 | rc = -EINVAL; | ||
1483 | break; | ||
1484 | |||
1485 | default: | ||
1486 | rc = -EINVAL; | ||
1487 | break; | ||
1488 | } | ||
1489 | return (rc); | ||
1490 | } | ||
1491 | |||
1492 | |||
1493 | static int isdn_net_ioctl(struct net_device *dev, | ||
1494 | struct ifreq *ifr, int cmd) | ||
1495 | { | ||
1496 | isdn_net_local *lp = netdev_priv(dev); | ||
1497 | |||
1498 | switch (lp->p_encap) { | ||
1499 | #ifdef CONFIG_ISDN_PPP | ||
1500 | case ISDN_NET_ENCAP_SYNCPPP: | ||
1501 | return isdn_ppp_dev_ioctl(dev, ifr, cmd); | ||
1502 | #endif | ||
1503 | case ISDN_NET_ENCAP_CISCOHDLCK: | ||
1504 | return isdn_ciscohdlck_dev_ioctl(dev, ifr, cmd); | ||
1505 | default: | ||
1506 | return -EINVAL; | ||
1507 | } | ||
1508 | } | ||
1509 | |||
1510 | /* called via cisco_timer.function */ | ||
1511 | static void | ||
1512 | isdn_net_ciscohdlck_slarp_send_keepalive(struct timer_list *t) | ||
1513 | { | ||
1514 | isdn_net_local *lp = from_timer(lp, t, cisco_timer); | ||
1515 | struct sk_buff *skb; | ||
1516 | unsigned char *p; | ||
1517 | unsigned long last_cisco_myseq = lp->cisco_myseq; | ||
1518 | int myseq_diff = 0; | ||
1519 | |||
1520 | if (!(lp->flags & ISDN_NET_CONNECTED) || lp->dialstate) { | ||
1521 | printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__); | ||
1522 | return; | ||
1523 | } | ||
1524 | lp->cisco_myseq++; | ||
1525 | |||
1526 | myseq_diff = (lp->cisco_myseq - lp->cisco_mineseen); | ||
1527 | if ((lp->cisco_line_state) && ((myseq_diff >= 3) || (myseq_diff <= -3))) { | ||
1528 | /* line up -> down */ | ||
1529 | lp->cisco_line_state = 0; | ||
1530 | printk(KERN_WARNING | ||
1531 | "UPDOWN: Line protocol on Interface %s," | ||
1532 | " changed state to down\n", lp->netdev->dev->name); | ||
1533 | /* should stop routing higher-level data across */ | ||
1534 | } else if ((!lp->cisco_line_state) && | ||
1535 | (myseq_diff >= 0) && (myseq_diff <= 2)) { | ||
1536 | /* line down -> up */ | ||
1537 | lp->cisco_line_state = 1; | ||
1538 | printk(KERN_WARNING | ||
1539 | "UPDOWN: Line protocol on Interface %s," | ||
1540 | " changed state to up\n", lp->netdev->dev->name); | ||
1541 | /* restart routing higher-level data across */ | ||
1542 | } | ||
1543 | |||
1544 | if (lp->cisco_debserint) | ||
1545 | printk(KERN_DEBUG "%s: HDLC " | ||
1546 | "myseq %lu, mineseen %lu%c, yourseen %lu, %s\n", | ||
1547 | lp->netdev->dev->name, last_cisco_myseq, lp->cisco_mineseen, | ||
1548 | ((last_cisco_myseq == lp->cisco_mineseen) ? '*' : 040), | ||
1549 | lp->cisco_yourseq, | ||
1550 | ((lp->cisco_line_state) ? "line up" : "line down")); | ||
1551 | |||
1552 | skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14); | ||
1553 | if (!skb) | ||
1554 | return; | ||
1555 | |||
1556 | p = skb_put(skb, 4 + 14); | ||
1557 | |||
1558 | /* cisco header */ | ||
1559 | *(u8 *)(p + 0) = CISCO_ADDR_UNICAST; | ||
1560 | *(u8 *)(p + 1) = CISCO_CTRL; | ||
1561 | *(__be16 *)(p + 2) = cpu_to_be16(CISCO_TYPE_SLARP); | ||
1562 | |||
1563 | /* slarp keepalive */ | ||
1564 | *(__be32 *)(p + 4) = cpu_to_be32(CISCO_SLARP_KEEPALIVE); | ||
1565 | *(__be32 *)(p + 8) = cpu_to_be32(lp->cisco_myseq); | ||
1566 | *(__be32 *)(p + 12) = cpu_to_be32(lp->cisco_yourseq); | ||
1567 | *(__be16 *)(p + 16) = cpu_to_be16(0xffff); // reliability, always 0xffff | ||
1568 | p += 18; | ||
1569 | |||
1570 | isdn_net_write_super(lp, skb); | ||
1571 | |||
1572 | lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ; | ||
1573 | |||
1574 | add_timer(&lp->cisco_timer); | ||
1575 | } | ||
1576 | |||
1577 | static void | ||
1578 | isdn_net_ciscohdlck_slarp_send_request(isdn_net_local *lp) | ||
1579 | { | ||
1580 | struct sk_buff *skb; | ||
1581 | unsigned char *p; | ||
1582 | |||
1583 | skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14); | ||
1584 | if (!skb) | ||
1585 | return; | ||
1586 | |||
1587 | p = skb_put(skb, 4 + 14); | ||
1588 | |||
1589 | /* cisco header */ | ||
1590 | *(u8 *)(p + 0) = CISCO_ADDR_UNICAST; | ||
1591 | *(u8 *)(p + 1) = CISCO_CTRL; | ||
1592 | *(__be16 *)(p + 2) = cpu_to_be16(CISCO_TYPE_SLARP); | ||
1593 | |||
1594 | /* slarp request */ | ||
1595 | *(__be32 *)(p + 4) = cpu_to_be32(CISCO_SLARP_REQUEST); | ||
1596 | *(__be32 *)(p + 8) = cpu_to_be32(0); // address | ||
1597 | *(__be32 *)(p + 12) = cpu_to_be32(0); // netmask | ||
1598 | *(__be16 *)(p + 16) = cpu_to_be16(0); // unused | ||
1599 | p += 18; | ||
1600 | |||
1601 | isdn_net_write_super(lp, skb); | ||
1602 | } | ||
1603 | |||
1604 | static void | ||
1605 | isdn_net_ciscohdlck_connected(isdn_net_local *lp) | ||
1606 | { | ||
1607 | lp->cisco_myseq = 0; | ||
1608 | lp->cisco_mineseen = 0; | ||
1609 | lp->cisco_yourseq = 0; | ||
1610 | lp->cisco_keepalive_period = ISDN_TIMER_KEEPINT; | ||
1611 | lp->cisco_last_slarp_in = 0; | ||
1612 | lp->cisco_line_state = 0; | ||
1613 | lp->cisco_debserint = 0; | ||
1614 | |||
1615 | /* send slarp request because interface/seq.no.s reset */ | ||
1616 | isdn_net_ciscohdlck_slarp_send_request(lp); | ||
1617 | |||
1618 | timer_setup(&lp->cisco_timer, | ||
1619 | isdn_net_ciscohdlck_slarp_send_keepalive, 0); | ||
1620 | lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ; | ||
1621 | add_timer(&lp->cisco_timer); | ||
1622 | } | ||
1623 | |||
1624 | static void | ||
1625 | isdn_net_ciscohdlck_disconnected(isdn_net_local *lp) | ||
1626 | { | ||
1627 | del_timer(&lp->cisco_timer); | ||
1628 | } | ||
1629 | |||
1630 | static void | ||
1631 | isdn_net_ciscohdlck_slarp_send_reply(isdn_net_local *lp) | ||
1632 | { | ||
1633 | struct sk_buff *skb; | ||
1634 | unsigned char *p; | ||
1635 | struct in_device *in_dev = NULL; | ||
1636 | __be32 addr = 0; /* local ipv4 address */ | ||
1637 | __be32 mask = 0; /* local netmask */ | ||
1638 | |||
1639 | if ((in_dev = lp->netdev->dev->ip_ptr) != NULL) { | ||
1640 | /* take primary(first) address of interface */ | ||
1641 | struct in_ifaddr *ifa = in_dev->ifa_list; | ||
1642 | if (ifa != NULL) { | ||
1643 | addr = ifa->ifa_local; | ||
1644 | mask = ifa->ifa_mask; | ||
1645 | } | ||
1646 | } | ||
1647 | |||
1648 | skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14); | ||
1649 | if (!skb) | ||
1650 | return; | ||
1651 | |||
1652 | p = skb_put(skb, 4 + 14); | ||
1653 | |||
1654 | /* cisco header */ | ||
1655 | *(u8 *)(p + 0) = CISCO_ADDR_UNICAST; | ||
1656 | *(u8 *)(p + 1) = CISCO_CTRL; | ||
1657 | *(__be16 *)(p + 2) = cpu_to_be16(CISCO_TYPE_SLARP); | ||
1658 | |||
1659 | /* slarp reply, send own ip/netmask; if values are nonsense remote | ||
1660 | * should think we are unable to provide it with an address via SLARP */ | ||
1661 | *(__be32 *)(p + 4) = cpu_to_be32(CISCO_SLARP_REPLY); | ||
1662 | *(__be32 *)(p + 8) = addr; // address | ||
1663 | *(__be32 *)(p + 12) = mask; // netmask | ||
1664 | *(__be16 *)(p + 16) = cpu_to_be16(0); // unused | ||
1665 | p += 18; | ||
1666 | |||
1667 | isdn_net_write_super(lp, skb); | ||
1668 | } | ||
1669 | |||
1670 | static void | ||
1671 | isdn_net_ciscohdlck_slarp_in(isdn_net_local *lp, struct sk_buff *skb) | ||
1672 | { | ||
1673 | unsigned char *p; | ||
1674 | int period; | ||
1675 | u32 code; | ||
1676 | u32 my_seq; | ||
1677 | u32 your_seq; | ||
1678 | __be32 local; | ||
1679 | __be32 *addr, *mask; | ||
1680 | |||
1681 | if (skb->len < 14) | ||
1682 | return; | ||
1683 | |||
1684 | p = skb->data; | ||
1685 | code = be32_to_cpup((__be32 *)p); | ||
1686 | p += 4; | ||
1687 | |||
1688 | switch (code) { | ||
1689 | case CISCO_SLARP_REQUEST: | ||
1690 | lp->cisco_yourseq = 0; | ||
1691 | isdn_net_ciscohdlck_slarp_send_reply(lp); | ||
1692 | break; | ||
1693 | case CISCO_SLARP_REPLY: | ||
1694 | addr = (__be32 *)p; | ||
1695 | mask = (__be32 *)(p + 4); | ||
1696 | if (*mask != cpu_to_be32(0xfffffffc)) | ||
1697 | goto slarp_reply_out; | ||
1698 | if ((*addr & cpu_to_be32(3)) == cpu_to_be32(0) || | ||
1699 | (*addr & cpu_to_be32(3)) == cpu_to_be32(3)) | ||
1700 | goto slarp_reply_out; | ||
1701 | local = *addr ^ cpu_to_be32(3); | ||
1702 | printk(KERN_INFO "%s: got slarp reply: remote ip: %pI4, local ip: %pI4 mask: %pI4\n", | ||
1703 | lp->netdev->dev->name, addr, &local, mask); | ||
1704 | break; | ||
1705 | slarp_reply_out: | ||
1706 | printk(KERN_INFO "%s: got invalid slarp reply (%pI4/%pI4) - ignored\n", | ||
1707 | lp->netdev->dev->name, addr, mask); | ||
1708 | break; | ||
1709 | case CISCO_SLARP_KEEPALIVE: | ||
1710 | period = (int)((jiffies - lp->cisco_last_slarp_in | ||
1711 | + HZ / 2 - 1) / HZ); | ||
1712 | if (lp->cisco_debserint && | ||
1713 | (period != lp->cisco_keepalive_period) && | ||
1714 | lp->cisco_last_slarp_in) { | ||
1715 | printk(KERN_DEBUG "%s: Keepalive period mismatch - " | ||
1716 | "is %d but should be %d.\n", | ||
1717 | lp->netdev->dev->name, period, | ||
1718 | lp->cisco_keepalive_period); | ||
1719 | } | ||
1720 | lp->cisco_last_slarp_in = jiffies; | ||
1721 | my_seq = be32_to_cpup((__be32 *)(p + 0)); | ||
1722 | your_seq = be32_to_cpup((__be32 *)(p + 4)); | ||
1723 | p += 10; | ||
1724 | lp->cisco_yourseq = my_seq; | ||
1725 | lp->cisco_mineseen = your_seq; | ||
1726 | break; | ||
1727 | } | ||
1728 | } | ||
1729 | |||
1730 | static void | ||
1731 | isdn_net_ciscohdlck_receive(isdn_net_local *lp, struct sk_buff *skb) | ||
1732 | { | ||
1733 | unsigned char *p; | ||
1734 | u8 addr; | ||
1735 | u8 ctrl; | ||
1736 | u16 type; | ||
1737 | |||
1738 | if (skb->len < 4) | ||
1739 | goto out_free; | ||
1740 | |||
1741 | p = skb->data; | ||
1742 | addr = *(u8 *)(p + 0); | ||
1743 | ctrl = *(u8 *)(p + 1); | ||
1744 | type = be16_to_cpup((__be16 *)(p + 2)); | ||
1745 | p += 4; | ||
1746 | skb_pull(skb, 4); | ||
1747 | |||
1748 | if (addr != CISCO_ADDR_UNICAST && addr != CISCO_ADDR_BROADCAST) { | ||
1749 | printk(KERN_WARNING "%s: Unknown Cisco addr 0x%02x\n", | ||
1750 | lp->netdev->dev->name, addr); | ||
1751 | goto out_free; | ||
1752 | } | ||
1753 | if (ctrl != CISCO_CTRL) { | ||
1754 | printk(KERN_WARNING "%s: Unknown Cisco ctrl 0x%02x\n", | ||
1755 | lp->netdev->dev->name, ctrl); | ||
1756 | goto out_free; | ||
1757 | } | ||
1758 | |||
1759 | switch (type) { | ||
1760 | case CISCO_TYPE_SLARP: | ||
1761 | isdn_net_ciscohdlck_slarp_in(lp, skb); | ||
1762 | goto out_free; | ||
1763 | case CISCO_TYPE_CDP: | ||
1764 | if (lp->cisco_debserint) | ||
1765 | printk(KERN_DEBUG "%s: Received CDP packet. use " | ||
1766 | "\"no cdp enable\" on cisco.\n", | ||
1767 | lp->netdev->dev->name); | ||
1768 | goto out_free; | ||
1769 | default: | ||
1770 | /* no special cisco protocol */ | ||
1771 | skb->protocol = htons(type); | ||
1772 | netif_rx(skb); | ||
1773 | return; | ||
1774 | } | ||
1775 | |||
1776 | out_free: | ||
1777 | kfree_skb(skb); | ||
1778 | } | ||
1779 | |||
1780 | /* | ||
1781 | * Got a packet from ISDN-Channel. | ||
1782 | */ | ||
1783 | static void | ||
1784 | isdn_net_receive(struct net_device *ndev, struct sk_buff *skb) | ||
1785 | { | ||
1786 | isdn_net_local *lp = netdev_priv(ndev); | ||
1787 | isdn_net_local *olp = lp; /* original 'lp' */ | ||
1788 | #ifdef CONFIG_ISDN_X25 | ||
1789 | struct concap_proto *cprot = lp->netdev->cprot; | ||
1790 | #endif | ||
1791 | lp->transcount += skb->len; | ||
1792 | |||
1793 | lp->stats.rx_packets++; | ||
1794 | lp->stats.rx_bytes += skb->len; | ||
1795 | if (lp->master) { | ||
1796 | /* Bundling: If device is a slave-device, deliver to master, also | ||
1797 | * handle master's statistics and hangup-timeout | ||
1798 | */ | ||
1799 | ndev = lp->master; | ||
1800 | lp = netdev_priv(ndev); | ||
1801 | lp->stats.rx_packets++; | ||
1802 | lp->stats.rx_bytes += skb->len; | ||
1803 | } | ||
1804 | skb->dev = ndev; | ||
1805 | skb->pkt_type = PACKET_HOST; | ||
1806 | skb_reset_mac_header(skb); | ||
1807 | #ifdef ISDN_DEBUG_NET_DUMP | ||
1808 | isdn_dumppkt("R:", skb->data, skb->len, 40); | ||
1809 | #endif | ||
1810 | switch (lp->p_encap) { | ||
1811 | case ISDN_NET_ENCAP_ETHER: | ||
1812 | /* Ethernet over ISDN */ | ||
1813 | olp->huptimer = 0; | ||
1814 | lp->huptimer = 0; | ||
1815 | skb->protocol = isdn_net_type_trans(skb, ndev); | ||
1816 | break; | ||
1817 | case ISDN_NET_ENCAP_UIHDLC: | ||
1818 | /* HDLC with UI-frame (for ispa with -h1 option) */ | ||
1819 | olp->huptimer = 0; | ||
1820 | lp->huptimer = 0; | ||
1821 | skb_pull(skb, 2); | ||
1822 | /* Fall through */ | ||
1823 | case ISDN_NET_ENCAP_RAWIP: | ||
1824 | /* RAW-IP without MAC-Header */ | ||
1825 | olp->huptimer = 0; | ||
1826 | lp->huptimer = 0; | ||
1827 | skb->protocol = htons(ETH_P_IP); | ||
1828 | break; | ||
1829 | case ISDN_NET_ENCAP_CISCOHDLCK: | ||
1830 | isdn_net_ciscohdlck_receive(lp, skb); | ||
1831 | return; | ||
1832 | case ISDN_NET_ENCAP_CISCOHDLC: | ||
1833 | /* CISCO-HDLC IP with type field and fake I-frame-header */ | ||
1834 | skb_pull(skb, 2); | ||
1835 | /* Fall through */ | ||
1836 | case ISDN_NET_ENCAP_IPTYP: | ||
1837 | /* IP with type field */ | ||
1838 | olp->huptimer = 0; | ||
1839 | lp->huptimer = 0; | ||
1840 | skb->protocol = *(__be16 *)&(skb->data[0]); | ||
1841 | skb_pull(skb, 2); | ||
1842 | if (*(unsigned short *) skb->data == 0xFFFF) | ||
1843 | skb->protocol = htons(ETH_P_802_3); | ||
1844 | break; | ||
1845 | #ifdef CONFIG_ISDN_PPP | ||
1846 | case ISDN_NET_ENCAP_SYNCPPP: | ||
1847 | /* huptimer is done in isdn_ppp_push_higher */ | ||
1848 | isdn_ppp_receive(lp->netdev, olp, skb); | ||
1849 | return; | ||
1850 | #endif | ||
1851 | |||
1852 | default: | ||
1853 | #ifdef CONFIG_ISDN_X25 | ||
1854 | /* try if there are generic sync_device receiver routines */ | ||
1855 | if (cprot) if (cprot->pops) | ||
1856 | if (cprot->pops->data_ind) { | ||
1857 | cprot->pops->data_ind(cprot, skb); | ||
1858 | return; | ||
1859 | }; | ||
1860 | #endif /* CONFIG_ISDN_X25 */ | ||
1861 | printk(KERN_WARNING "%s: unknown encapsulation, dropping\n", | ||
1862 | lp->netdev->dev->name); | ||
1863 | kfree_skb(skb); | ||
1864 | return; | ||
1865 | } | ||
1866 | |||
1867 | netif_rx(skb); | ||
1868 | return; | ||
1869 | } | ||
1870 | |||
1871 | /* | ||
1872 | * A packet arrived via ISDN. Search interface-chain for a corresponding | ||
1873 | * interface. If found, deliver packet to receiver-function and return 1, | ||
1874 | * else return 0. | ||
1875 | */ | ||
1876 | int | ||
1877 | isdn_net_rcv_skb(int idx, struct sk_buff *skb) | ||
1878 | { | ||
1879 | isdn_net_dev *p = dev->rx_netdev[idx]; | ||
1880 | |||
1881 | if (p) { | ||
1882 | isdn_net_local *lp = p->local; | ||
1883 | if ((lp->flags & ISDN_NET_CONNECTED) && | ||
1884 | (!lp->dialstate)) { | ||
1885 | isdn_net_receive(p->dev, skb); | ||
1886 | return 1; | ||
1887 | } | ||
1888 | } | ||
1889 | return 0; | ||
1890 | } | ||
1891 | |||
1892 | /* | ||
1893 | * build an header | ||
1894 | * depends on encaps that is being used. | ||
1895 | */ | ||
1896 | |||
1897 | static int isdn_net_header(struct sk_buff *skb, struct net_device *dev, | ||
1898 | unsigned short type, | ||
1899 | const void *daddr, const void *saddr, unsigned plen) | ||
1900 | { | ||
1901 | isdn_net_local *lp = netdev_priv(dev); | ||
1902 | unsigned char *p; | ||
1903 | int len = 0; | ||
1904 | |||
1905 | switch (lp->p_encap) { | ||
1906 | case ISDN_NET_ENCAP_ETHER: | ||
1907 | len = eth_header(skb, dev, type, daddr, saddr, plen); | ||
1908 | break; | ||
1909 | #ifdef CONFIG_ISDN_PPP | ||
1910 | case ISDN_NET_ENCAP_SYNCPPP: | ||
1911 | /* stick on a fake header to keep fragmentation code happy. */ | ||
1912 | len = IPPP_MAX_HEADER; | ||
1913 | skb_push(skb, len); | ||
1914 | break; | ||
1915 | #endif | ||
1916 | case ISDN_NET_ENCAP_RAWIP: | ||
1917 | printk(KERN_WARNING "isdn_net_header called with RAW_IP!\n"); | ||
1918 | len = 0; | ||
1919 | break; | ||
1920 | case ISDN_NET_ENCAP_IPTYP: | ||
1921 | /* ethernet type field */ | ||
1922 | *((__be16 *)skb_push(skb, 2)) = htons(type); | ||
1923 | len = 2; | ||
1924 | break; | ||
1925 | case ISDN_NET_ENCAP_UIHDLC: | ||
1926 | /* HDLC with UI-Frames (for ispa with -h1 option) */ | ||
1927 | *((__be16 *)skb_push(skb, 2)) = htons(0x0103); | ||
1928 | len = 2; | ||
1929 | break; | ||
1930 | case ISDN_NET_ENCAP_CISCOHDLC: | ||
1931 | case ISDN_NET_ENCAP_CISCOHDLCK: | ||
1932 | p = skb_push(skb, 4); | ||
1933 | *(u8 *)(p + 0) = CISCO_ADDR_UNICAST; | ||
1934 | *(u8 *)(p + 1) = CISCO_CTRL; | ||
1935 | *(__be16 *)(p + 2) = cpu_to_be16(type); | ||
1936 | p += 4; | ||
1937 | len = 4; | ||
1938 | break; | ||
1939 | #ifdef CONFIG_ISDN_X25 | ||
1940 | default: | ||
1941 | /* try if there are generic concap protocol routines */ | ||
1942 | if (lp->netdev->cprot) { | ||
1943 | printk(KERN_WARNING "isdn_net_header called with concap_proto!\n"); | ||
1944 | len = 0; | ||
1945 | break; | ||
1946 | } | ||
1947 | break; | ||
1948 | #endif /* CONFIG_ISDN_X25 */ | ||
1949 | } | ||
1950 | return len; | ||
1951 | } | ||
1952 | |||
1953 | static int isdn_header_cache(const struct neighbour *neigh, struct hh_cache *hh, | ||
1954 | __be16 type) | ||
1955 | { | ||
1956 | const struct net_device *dev = neigh->dev; | ||
1957 | isdn_net_local *lp = netdev_priv(dev); | ||
1958 | |||
1959 | if (lp->p_encap == ISDN_NET_ENCAP_ETHER) | ||
1960 | return eth_header_cache(neigh, hh, type); | ||
1961 | return -1; | ||
1962 | } | ||
1963 | |||
1964 | static void isdn_header_cache_update(struct hh_cache *hh, | ||
1965 | const struct net_device *dev, | ||
1966 | const unsigned char *haddr) | ||
1967 | { | ||
1968 | isdn_net_local *lp = netdev_priv(dev); | ||
1969 | if (lp->p_encap == ISDN_NET_ENCAP_ETHER) | ||
1970 | eth_header_cache_update(hh, dev, haddr); | ||
1971 | } | ||
1972 | |||
1973 | static const struct header_ops isdn_header_ops = { | ||
1974 | .create = isdn_net_header, | ||
1975 | .cache = isdn_header_cache, | ||
1976 | .cache_update = isdn_header_cache_update, | ||
1977 | }; | ||
1978 | |||
1979 | /* | ||
1980 | * Interface-setup. (just after registering a new interface) | ||
1981 | */ | ||
1982 | static int | ||
1983 | isdn_net_init(struct net_device *ndev) | ||
1984 | { | ||
1985 | ushort max_hlhdr_len = 0; | ||
1986 | int drvidx; | ||
1987 | |||
1988 | /* | ||
1989 | * up till binding we ask the protocol layer to reserve as much | ||
1990 | * as we might need for HL layer | ||
1991 | */ | ||
1992 | |||
1993 | for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++) | ||
1994 | if (dev->drv[drvidx]) | ||
1995 | if (max_hlhdr_len < dev->drv[drvidx]->interface->hl_hdrlen) | ||
1996 | max_hlhdr_len = dev->drv[drvidx]->interface->hl_hdrlen; | ||
1997 | |||
1998 | ndev->hard_header_len = ETH_HLEN + max_hlhdr_len; | ||
1999 | return 0; | ||
2000 | } | ||
2001 | |||
2002 | static void | ||
2003 | isdn_net_swapbind(int drvidx) | ||
2004 | { | ||
2005 | isdn_net_dev *p; | ||
2006 | |||
2007 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2008 | printk(KERN_DEBUG "n_fi: swapping ch of %d\n", drvidx); | ||
2009 | #endif | ||
2010 | p = dev->netdev; | ||
2011 | while (p) { | ||
2012 | if (p->local->pre_device == drvidx) | ||
2013 | switch (p->local->pre_channel) { | ||
2014 | case 0: | ||
2015 | p->local->pre_channel = 1; | ||
2016 | break; | ||
2017 | case 1: | ||
2018 | p->local->pre_channel = 0; | ||
2019 | break; | ||
2020 | } | ||
2021 | p = (isdn_net_dev *) p->next; | ||
2022 | } | ||
2023 | } | ||
2024 | |||
2025 | static void | ||
2026 | isdn_net_swap_usage(int i1, int i2) | ||
2027 | { | ||
2028 | int u1 = dev->usage[i1] & ISDN_USAGE_EXCLUSIVE; | ||
2029 | int u2 = dev->usage[i2] & ISDN_USAGE_EXCLUSIVE; | ||
2030 | |||
2031 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2032 | printk(KERN_DEBUG "n_fi: usage of %d and %d\n", i1, i2); | ||
2033 | #endif | ||
2034 | dev->usage[i1] &= ~ISDN_USAGE_EXCLUSIVE; | ||
2035 | dev->usage[i1] |= u2; | ||
2036 | dev->usage[i2] &= ~ISDN_USAGE_EXCLUSIVE; | ||
2037 | dev->usage[i2] |= u1; | ||
2038 | isdn_info_update(); | ||
2039 | } | ||
2040 | |||
2041 | /* | ||
2042 | * An incoming call-request has arrived. | ||
2043 | * Search the interface-chain for an appropriate interface. | ||
2044 | * If found, connect the interface to the ISDN-channel and initiate | ||
2045 | * D- and B-Channel-setup. If secure-flag is set, accept only | ||
2046 | * configured phone-numbers. If callback-flag is set, initiate | ||
2047 | * callback-dialing. | ||
2048 | * | ||
2049 | * Return-Value: 0 = No appropriate interface for this call. | ||
2050 | * 1 = Call accepted | ||
2051 | * 2 = Reject call, wait cbdelay, then call back | ||
2052 | * 3 = Reject call | ||
2053 | * 4 = Wait cbdelay, then call back | ||
2054 | * 5 = No appropriate interface for this call, | ||
2055 | * would eventually match if CID was longer. | ||
2056 | */ | ||
2057 | |||
2058 | int | ||
2059 | isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup) | ||
2060 | { | ||
2061 | char *eaz; | ||
2062 | int si1; | ||
2063 | int si2; | ||
2064 | int ematch; | ||
2065 | int wret; | ||
2066 | int swapped; | ||
2067 | int sidx = 0; | ||
2068 | u_long flags; | ||
2069 | isdn_net_dev *p; | ||
2070 | isdn_net_phone *n; | ||
2071 | char nr[ISDN_MSNLEN]; | ||
2072 | char *my_eaz; | ||
2073 | |||
2074 | /* Search name in netdev-chain */ | ||
2075 | if (!setup->phone[0]) { | ||
2076 | nr[0] = '0'; | ||
2077 | nr[1] = '\0'; | ||
2078 | printk(KERN_INFO "isdn_net: Incoming call without OAD, assuming '0'\n"); | ||
2079 | } else | ||
2080 | strlcpy(nr, setup->phone, ISDN_MSNLEN); | ||
2081 | si1 = (int) setup->si1; | ||
2082 | si2 = (int) setup->si2; | ||
2083 | if (!setup->eazmsn[0]) { | ||
2084 | printk(KERN_WARNING "isdn_net: Incoming call without CPN, assuming '0'\n"); | ||
2085 | eaz = "0"; | ||
2086 | } else | ||
2087 | eaz = setup->eazmsn; | ||
2088 | if (dev->net_verbose > 1) | ||
2089 | printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz); | ||
2090 | /* Accept DATA and VOICE calls at this stage | ||
2091 | * local eaz is checked later for allowed call types | ||
2092 | */ | ||
2093 | if ((si1 != 7) && (si1 != 1)) { | ||
2094 | if (dev->net_verbose > 1) | ||
2095 | printk(KERN_INFO "isdn_net: Service-Indicator not 1 or 7, ignored\n"); | ||
2096 | return 0; | ||
2097 | } | ||
2098 | n = (isdn_net_phone *) 0; | ||
2099 | p = dev->netdev; | ||
2100 | ematch = wret = swapped = 0; | ||
2101 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2102 | printk(KERN_DEBUG "n_fi: di=%d ch=%d idx=%d usg=%d\n", di, ch, idx, | ||
2103 | dev->usage[idx]); | ||
2104 | #endif | ||
2105 | while (p) { | ||
2106 | int matchret; | ||
2107 | isdn_net_local *lp = p->local; | ||
2108 | |||
2109 | /* If last check has triggered as binding-swap, revert it */ | ||
2110 | switch (swapped) { | ||
2111 | case 2: | ||
2112 | isdn_net_swap_usage(idx, sidx); | ||
2113 | /* fall through */ | ||
2114 | case 1: | ||
2115 | isdn_net_swapbind(di); | ||
2116 | break; | ||
2117 | } | ||
2118 | swapped = 0; | ||
2119 | /* check acceptable call types for DOV */ | ||
2120 | my_eaz = isdn_map_eaz2msn(lp->msn, di); | ||
2121 | if (si1 == 1) { /* it's a DOV call, check if we allow it */ | ||
2122 | if (*my_eaz == 'v' || *my_eaz == 'V' || | ||
2123 | *my_eaz == 'b' || *my_eaz == 'B') | ||
2124 | my_eaz++; /* skip to allow a match */ | ||
2125 | else | ||
2126 | my_eaz = NULL; /* force non match */ | ||
2127 | } else { /* it's a DATA call, check if we allow it */ | ||
2128 | if (*my_eaz == 'b' || *my_eaz == 'B') | ||
2129 | my_eaz++; /* skip to allow a match */ | ||
2130 | } | ||
2131 | if (my_eaz) | ||
2132 | matchret = isdn_msncmp(eaz, my_eaz); | ||
2133 | else | ||
2134 | matchret = 1; | ||
2135 | if (!matchret) | ||
2136 | ematch = 1; | ||
2137 | |||
2138 | /* Remember if more numbers eventually can match */ | ||
2139 | if (matchret > wret) | ||
2140 | wret = matchret; | ||
2141 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2142 | printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n", | ||
2143 | p->dev->name, lp->msn, lp->flags, lp->dialstate); | ||
2144 | #endif | ||
2145 | if ((!matchret) && /* EAZ is matching */ | ||
2146 | (((!(lp->flags & ISDN_NET_CONNECTED)) && /* but not connected */ | ||
2147 | (USG_NONE(dev->usage[idx]))) || /* and ch. unused or */ | ||
2148 | ((((lp->dialstate == 4) || (lp->dialstate == 12)) && /* if dialing */ | ||
2149 | (!(lp->flags & ISDN_NET_CALLBACK))) /* but no callback */ | ||
2150 | ))) | ||
2151 | { | ||
2152 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2153 | printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n", | ||
2154 | lp->pre_device, lp->pre_channel); | ||
2155 | #endif | ||
2156 | if (dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) { | ||
2157 | if ((lp->pre_channel != ch) || | ||
2158 | (lp->pre_device != di)) { | ||
2159 | /* Here we got a problem: | ||
2160 | * If using an ICN-Card, an incoming call is always signaled on | ||
2161 | * on the first channel of the card, if both channels are | ||
2162 | * down. However this channel may be bound exclusive. If the | ||
2163 | * second channel is free, this call should be accepted. | ||
2164 | * The solution is horribly but it runs, so what: | ||
2165 | * We exchange the exclusive bindings of the two channels, the | ||
2166 | * corresponding variables in the interface-structs. | ||
2167 | */ | ||
2168 | if (ch == 0) { | ||
2169 | sidx = isdn_dc2minor(di, 1); | ||
2170 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2171 | printk(KERN_DEBUG "n_fi: ch is 0\n"); | ||
2172 | #endif | ||
2173 | if (USG_NONE(dev->usage[sidx])) { | ||
2174 | /* Second Channel is free, now see if it is bound | ||
2175 | * exclusive too. */ | ||
2176 | if (dev->usage[sidx] & ISDN_USAGE_EXCLUSIVE) { | ||
2177 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2178 | printk(KERN_DEBUG "n_fi: 2nd channel is down and bound\n"); | ||
2179 | #endif | ||
2180 | /* Yes, swap bindings only, if the original | ||
2181 | * binding is bound to channel 1 of this driver */ | ||
2182 | if ((lp->pre_device == di) && | ||
2183 | (lp->pre_channel == 1)) { | ||
2184 | isdn_net_swapbind(di); | ||
2185 | swapped = 1; | ||
2186 | } else { | ||
2187 | /* ... else iterate next device */ | ||
2188 | p = (isdn_net_dev *) p->next; | ||
2189 | continue; | ||
2190 | } | ||
2191 | } else { | ||
2192 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2193 | printk(KERN_DEBUG "n_fi: 2nd channel is down and unbound\n"); | ||
2194 | #endif | ||
2195 | /* No, swap always and swap excl-usage also */ | ||
2196 | isdn_net_swap_usage(idx, sidx); | ||
2197 | isdn_net_swapbind(di); | ||
2198 | swapped = 2; | ||
2199 | } | ||
2200 | /* Now check for exclusive binding again */ | ||
2201 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2202 | printk(KERN_DEBUG "n_fi: final check\n"); | ||
2203 | #endif | ||
2204 | if ((dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) && | ||
2205 | ((lp->pre_channel != ch) || | ||
2206 | (lp->pre_device != di))) { | ||
2207 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2208 | printk(KERN_DEBUG "n_fi: final check failed\n"); | ||
2209 | #endif | ||
2210 | p = (isdn_net_dev *) p->next; | ||
2211 | continue; | ||
2212 | } | ||
2213 | } | ||
2214 | } else { | ||
2215 | /* We are already on the second channel, so nothing to do */ | ||
2216 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2217 | printk(KERN_DEBUG "n_fi: already on 2nd channel\n"); | ||
2218 | #endif | ||
2219 | } | ||
2220 | } | ||
2221 | } | ||
2222 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2223 | printk(KERN_DEBUG "n_fi: match2\n"); | ||
2224 | #endif | ||
2225 | n = lp->phone[0]; | ||
2226 | if (lp->flags & ISDN_NET_SECURE) { | ||
2227 | while (n) { | ||
2228 | if (!isdn_msncmp(nr, n->num)) | ||
2229 | break; | ||
2230 | n = (isdn_net_phone *) n->next; | ||
2231 | } | ||
2232 | } | ||
2233 | if (n || (!(lp->flags & ISDN_NET_SECURE))) { | ||
2234 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2235 | printk(KERN_DEBUG "n_fi: match3\n"); | ||
2236 | #endif | ||
2237 | /* matching interface found */ | ||
2238 | |||
2239 | /* | ||
2240 | * Is the state STOPPED? | ||
2241 | * If so, no dialin is allowed, | ||
2242 | * so reject actively. | ||
2243 | * */ | ||
2244 | if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) { | ||
2245 | printk(KERN_INFO "incoming call, interface %s `stopped' -> rejected\n", | ||
2246 | p->dev->name); | ||
2247 | return 3; | ||
2248 | } | ||
2249 | /* | ||
2250 | * Is the interface up? | ||
2251 | * If not, reject the call actively. | ||
2252 | */ | ||
2253 | if (!isdn_net_device_started(p)) { | ||
2254 | printk(KERN_INFO "%s: incoming call, interface down -> rejected\n", | ||
2255 | p->dev->name); | ||
2256 | return 3; | ||
2257 | } | ||
2258 | /* Interface is up, now see if it's a slave. If so, see if | ||
2259 | * it's master and parent slave is online. If not, reject the call. | ||
2260 | */ | ||
2261 | if (lp->master) { | ||
2262 | isdn_net_local *mlp = ISDN_MASTER_PRIV(lp); | ||
2263 | printk(KERN_DEBUG "ICALLslv: %s\n", p->dev->name); | ||
2264 | printk(KERN_DEBUG "master=%s\n", lp->master->name); | ||
2265 | if (mlp->flags & ISDN_NET_CONNECTED) { | ||
2266 | printk(KERN_DEBUG "master online\n"); | ||
2267 | /* Master is online, find parent-slave (master if first slave) */ | ||
2268 | while (mlp->slave) { | ||
2269 | if (ISDN_SLAVE_PRIV(mlp) == lp) | ||
2270 | break; | ||
2271 | mlp = ISDN_SLAVE_PRIV(mlp); | ||
2272 | } | ||
2273 | } else | ||
2274 | printk(KERN_DEBUG "master offline\n"); | ||
2275 | /* Found parent, if it's offline iterate next device */ | ||
2276 | printk(KERN_DEBUG "mlpf: %d\n", mlp->flags & ISDN_NET_CONNECTED); | ||
2277 | if (!(mlp->flags & ISDN_NET_CONNECTED)) { | ||
2278 | p = (isdn_net_dev *) p->next; | ||
2279 | continue; | ||
2280 | } | ||
2281 | } | ||
2282 | if (lp->flags & ISDN_NET_CALLBACK) { | ||
2283 | int chi; | ||
2284 | /* | ||
2285 | * Is the state MANUAL? | ||
2286 | * If so, no callback can be made, | ||
2287 | * so reject actively. | ||
2288 | * */ | ||
2289 | if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) { | ||
2290 | printk(KERN_INFO "incoming call for callback, interface %s `off' -> rejected\n", | ||
2291 | p->dev->name); | ||
2292 | return 3; | ||
2293 | } | ||
2294 | printk(KERN_DEBUG "%s: call from %s -> %s, start callback\n", | ||
2295 | p->dev->name, nr, eaz); | ||
2296 | if (lp->phone[1]) { | ||
2297 | /* Grab a free ISDN-Channel */ | ||
2298 | spin_lock_irqsave(&dev->lock, flags); | ||
2299 | if ((chi = | ||
2300 | isdn_get_free_channel( | ||
2301 | ISDN_USAGE_NET, | ||
2302 | lp->l2_proto, | ||
2303 | lp->l3_proto, | ||
2304 | lp->pre_device, | ||
2305 | lp->pre_channel, | ||
2306 | lp->msn) | ||
2307 | ) < 0) { | ||
2308 | |||
2309 | printk(KERN_WARNING "isdn_net_find_icall: No channel for %s\n", | ||
2310 | p->dev->name); | ||
2311 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2312 | return 0; | ||
2313 | } | ||
2314 | /* Setup dialstate. */ | ||
2315 | lp->dtimer = 0; | ||
2316 | lp->dialstate = 11; | ||
2317 | /* Connect interface with channel */ | ||
2318 | isdn_net_bind_channel(lp, chi); | ||
2319 | #ifdef CONFIG_ISDN_PPP | ||
2320 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) | ||
2321 | if (isdn_ppp_bind(lp) < 0) { | ||
2322 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2323 | isdn_net_unbind_channel(lp); | ||
2324 | return 0; | ||
2325 | } | ||
2326 | #endif | ||
2327 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2328 | /* Initiate dialing by returning 2 or 4 */ | ||
2329 | return (lp->flags & ISDN_NET_CBHUP) ? 2 : 4; | ||
2330 | } else | ||
2331 | printk(KERN_WARNING "isdn_net: %s: No phone number\n", | ||
2332 | p->dev->name); | ||
2333 | return 0; | ||
2334 | } else { | ||
2335 | printk(KERN_DEBUG "%s: call from %s -> %s accepted\n", | ||
2336 | p->dev->name, nr, eaz); | ||
2337 | /* if this interface is dialing, it does it probably on a different | ||
2338 | device, so free this device */ | ||
2339 | if ((lp->dialstate == 4) || (lp->dialstate == 12)) { | ||
2340 | #ifdef CONFIG_ISDN_PPP | ||
2341 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) | ||
2342 | isdn_ppp_free(lp); | ||
2343 | #endif | ||
2344 | isdn_net_lp_disconnected(lp); | ||
2345 | isdn_free_channel(lp->isdn_device, lp->isdn_channel, | ||
2346 | ISDN_USAGE_NET); | ||
2347 | } | ||
2348 | spin_lock_irqsave(&dev->lock, flags); | ||
2349 | dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE; | ||
2350 | dev->usage[idx] |= ISDN_USAGE_NET; | ||
2351 | strcpy(dev->num[idx], nr); | ||
2352 | isdn_info_update(); | ||
2353 | dev->st_netdev[idx] = lp->netdev; | ||
2354 | lp->isdn_device = di; | ||
2355 | lp->isdn_channel = ch; | ||
2356 | lp->ppp_slot = -1; | ||
2357 | lp->flags |= ISDN_NET_CONNECTED; | ||
2358 | lp->dialstate = 7; | ||
2359 | lp->dtimer = 0; | ||
2360 | lp->outgoing = 0; | ||
2361 | lp->huptimer = 0; | ||
2362 | lp->hupflags |= ISDN_WAITCHARGE; | ||
2363 | lp->hupflags &= ~ISDN_HAVECHARGE; | ||
2364 | #ifdef CONFIG_ISDN_PPP | ||
2365 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) { | ||
2366 | if (isdn_ppp_bind(lp) < 0) { | ||
2367 | isdn_net_unbind_channel(lp); | ||
2368 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2369 | return 0; | ||
2370 | } | ||
2371 | } | ||
2372 | #endif | ||
2373 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2374 | return 1; | ||
2375 | } | ||
2376 | } | ||
2377 | } | ||
2378 | p = (isdn_net_dev *) p->next; | ||
2379 | } | ||
2380 | /* If none of configured EAZ/MSN matched and not verbose, be silent */ | ||
2381 | if (!ematch || dev->net_verbose) | ||
2382 | printk(KERN_INFO "isdn_net: call from %s -> %d %s ignored\n", nr, di, eaz); | ||
2383 | return (wret == 2) ? 5 : 0; | ||
2384 | } | ||
2385 | |||
2386 | /* | ||
2387 | * Search list of net-interfaces for an interface with given name. | ||
2388 | */ | ||
2389 | isdn_net_dev * | ||
2390 | isdn_net_findif(char *name) | ||
2391 | { | ||
2392 | isdn_net_dev *p = dev->netdev; | ||
2393 | |||
2394 | while (p) { | ||
2395 | if (!strcmp(p->dev->name, name)) | ||
2396 | return p; | ||
2397 | p = (isdn_net_dev *) p->next; | ||
2398 | } | ||
2399 | return (isdn_net_dev *) NULL; | ||
2400 | } | ||
2401 | |||
2402 | /* | ||
2403 | * Force a net-interface to dial out. | ||
2404 | * This is called from the userlevel-routine below or | ||
2405 | * from isdn_net_start_xmit(). | ||
2406 | */ | ||
2407 | static int | ||
2408 | isdn_net_force_dial_lp(isdn_net_local *lp) | ||
2409 | { | ||
2410 | if ((!(lp->flags & ISDN_NET_CONNECTED)) && !lp->dialstate) { | ||
2411 | int chi; | ||
2412 | if (lp->phone[1]) { | ||
2413 | ulong flags; | ||
2414 | |||
2415 | /* Grab a free ISDN-Channel */ | ||
2416 | spin_lock_irqsave(&dev->lock, flags); | ||
2417 | if ((chi = isdn_get_free_channel( | ||
2418 | ISDN_USAGE_NET, | ||
2419 | lp->l2_proto, | ||
2420 | lp->l3_proto, | ||
2421 | lp->pre_device, | ||
2422 | lp->pre_channel, | ||
2423 | lp->msn)) < 0) { | ||
2424 | printk(KERN_WARNING "isdn_net_force_dial: No channel for %s\n", | ||
2425 | lp->netdev->dev->name); | ||
2426 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2427 | return -EAGAIN; | ||
2428 | } | ||
2429 | lp->dialstate = 1; | ||
2430 | /* Connect interface with channel */ | ||
2431 | isdn_net_bind_channel(lp, chi); | ||
2432 | #ifdef CONFIG_ISDN_PPP | ||
2433 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) | ||
2434 | if (isdn_ppp_bind(lp) < 0) { | ||
2435 | isdn_net_unbind_channel(lp); | ||
2436 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2437 | return -EAGAIN; | ||
2438 | } | ||
2439 | #endif | ||
2440 | /* Initiate dialing */ | ||
2441 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2442 | isdn_net_dial(); | ||
2443 | return 0; | ||
2444 | } else | ||
2445 | return -EINVAL; | ||
2446 | } else | ||
2447 | return -EBUSY; | ||
2448 | } | ||
2449 | |||
2450 | /* | ||
2451 | * This is called from certain upper protocol layers (multilink ppp | ||
2452 | * and x25iface encapsulation module) that want to initiate dialing | ||
2453 | * themselves. | ||
2454 | */ | ||
2455 | int | ||
2456 | isdn_net_dial_req(isdn_net_local *lp) | ||
2457 | { | ||
2458 | /* is there a better error code? */ | ||
2459 | if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) return -EBUSY; | ||
2460 | |||
2461 | return isdn_net_force_dial_lp(lp); | ||
2462 | } | ||
2463 | |||
2464 | /* | ||
2465 | * Force a net-interface to dial out. | ||
2466 | * This is always called from within userspace (ISDN_IOCTL_NET_DIAL). | ||
2467 | */ | ||
2468 | int | ||
2469 | isdn_net_force_dial(char *name) | ||
2470 | { | ||
2471 | isdn_net_dev *p = isdn_net_findif(name); | ||
2472 | |||
2473 | if (!p) | ||
2474 | return -ENODEV; | ||
2475 | return (isdn_net_force_dial_lp(p->local)); | ||
2476 | } | ||
2477 | |||
2478 | /* The ISDN-specific entries in the device structure. */ | ||
2479 | static const struct net_device_ops isdn_netdev_ops = { | ||
2480 | .ndo_init = isdn_net_init, | ||
2481 | .ndo_open = isdn_net_open, | ||
2482 | .ndo_stop = isdn_net_close, | ||
2483 | .ndo_do_ioctl = isdn_net_ioctl, | ||
2484 | |||
2485 | .ndo_start_xmit = isdn_net_start_xmit, | ||
2486 | .ndo_get_stats = isdn_net_get_stats, | ||
2487 | .ndo_tx_timeout = isdn_net_tx_timeout, | ||
2488 | }; | ||
2489 | |||
2490 | /* | ||
2491 | * Helper for alloc_netdev() | ||
2492 | */ | ||
2493 | static void _isdn_setup(struct net_device *dev) | ||
2494 | { | ||
2495 | isdn_net_local *lp = netdev_priv(dev); | ||
2496 | |||
2497 | ether_setup(dev); | ||
2498 | |||
2499 | /* Setup the generic properties */ | ||
2500 | dev->flags = IFF_NOARP | IFF_POINTOPOINT; | ||
2501 | |||
2502 | /* isdn prepends a header in the tx path, can't share skbs */ | ||
2503 | dev->priv_flags &= ~IFF_TX_SKB_SHARING; | ||
2504 | dev->header_ops = NULL; | ||
2505 | dev->netdev_ops = &isdn_netdev_ops; | ||
2506 | |||
2507 | /* for clients with MPPP maybe higher values better */ | ||
2508 | dev->tx_queue_len = 30; | ||
2509 | |||
2510 | lp->p_encap = ISDN_NET_ENCAP_RAWIP; | ||
2511 | lp->magic = ISDN_NET_MAGIC; | ||
2512 | lp->last = lp; | ||
2513 | lp->next = lp; | ||
2514 | lp->isdn_device = -1; | ||
2515 | lp->isdn_channel = -1; | ||
2516 | lp->pre_device = -1; | ||
2517 | lp->pre_channel = -1; | ||
2518 | lp->exclusive = -1; | ||
2519 | lp->ppp_slot = -1; | ||
2520 | lp->pppbind = -1; | ||
2521 | skb_queue_head_init(&lp->super_tx_queue); | ||
2522 | lp->l2_proto = ISDN_PROTO_L2_X75I; | ||
2523 | lp->l3_proto = ISDN_PROTO_L3_TRANS; | ||
2524 | lp->triggercps = 6000; | ||
2525 | lp->slavedelay = 10 * HZ; | ||
2526 | lp->hupflags = ISDN_INHUP; /* Do hangup even on incoming calls */ | ||
2527 | lp->onhtime = 10; /* Default hangup-time for saving costs */ | ||
2528 | lp->dialmax = 1; | ||
2529 | /* Hangup before Callback, manual dial */ | ||
2530 | lp->flags = ISDN_NET_CBHUP | ISDN_NET_DM_MANUAL; | ||
2531 | lp->cbdelay = 25; /* Wait 5 secs before Callback */ | ||
2532 | lp->dialtimeout = -1; /* Infinite Dial-Timeout */ | ||
2533 | lp->dialwait = 5 * HZ; /* Wait 5 sec. after failed dial */ | ||
2534 | lp->dialstarted = 0; /* Jiffies of last dial-start */ | ||
2535 | lp->dialwait_timer = 0; /* Jiffies of earliest next dial-start */ | ||
2536 | } | ||
2537 | |||
2538 | /* | ||
2539 | * Allocate a new network-interface and initialize its data structures. | ||
2540 | */ | ||
2541 | char * | ||
2542 | isdn_net_new(char *name, struct net_device *master) | ||
2543 | { | ||
2544 | isdn_net_dev *netdev; | ||
2545 | |||
2546 | /* Avoid creating an existing interface */ | ||
2547 | if (isdn_net_findif(name)) { | ||
2548 | printk(KERN_WARNING "isdn_net: interface %s already exists\n", name); | ||
2549 | return NULL; | ||
2550 | } | ||
2551 | if (name == NULL) | ||
2552 | return NULL; | ||
2553 | if (!(netdev = kzalloc(sizeof(isdn_net_dev), GFP_KERNEL))) { | ||
2554 | printk(KERN_WARNING "isdn_net: Could not allocate net-device\n"); | ||
2555 | return NULL; | ||
2556 | } | ||
2557 | netdev->dev = alloc_netdev(sizeof(isdn_net_local), name, | ||
2558 | NET_NAME_UNKNOWN, _isdn_setup); | ||
2559 | if (!netdev->dev) { | ||
2560 | printk(KERN_WARNING "isdn_net: Could not allocate network device\n"); | ||
2561 | kfree(netdev); | ||
2562 | return NULL; | ||
2563 | } | ||
2564 | netdev->local = netdev_priv(netdev->dev); | ||
2565 | |||
2566 | if (master) { | ||
2567 | /* Device shall be a slave */ | ||
2568 | struct net_device *p = MASTER_TO_SLAVE(master); | ||
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 = MASTER_TO_SLAVE(p); | ||
2576 | } | ||
2577 | MASTER_TO_SLAVE(q) = netdev->dev; | ||
2578 | } else { | ||
2579 | /* Device shall be a master */ | ||
2580 | /* | ||
2581 | * Watchdog timer (currently) for master only. | ||
2582 | */ | ||
2583 | netdev->dev->watchdog_timeo = ISDN_NET_TX_TIMEOUT; | ||
2584 | if (register_netdev(netdev->dev) != 0) { | ||
2585 | printk(KERN_WARNING "isdn_net: Could not register net-device\n"); | ||
2586 | free_netdev(netdev->dev); | ||
2587 | kfree(netdev); | ||
2588 | return NULL; | ||
2589 | } | ||
2590 | } | ||
2591 | netdev->queue = netdev->local; | ||
2592 | spin_lock_init(&netdev->queue_lock); | ||
2593 | |||
2594 | netdev->local->netdev = netdev; | ||
2595 | |||
2596 | INIT_WORK(&netdev->local->tqueue, isdn_net_softint); | ||
2597 | spin_lock_init(&netdev->local->xmit_lock); | ||
2598 | |||
2599 | /* Put into to netdev-chain */ | ||
2600 | netdev->next = (void *) dev->netdev; | ||
2601 | dev->netdev = netdev; | ||
2602 | return netdev->dev->name; | ||
2603 | } | ||
2604 | |||
2605 | char * | ||
2606 | isdn_net_newslave(char *parm) | ||
2607 | { | ||
2608 | char *p = strchr(parm, ','); | ||
2609 | isdn_net_dev *n; | ||
2610 | char newname[10]; | ||
2611 | |||
2612 | if (p) { | ||
2613 | /* Slave-Name MUST not be empty or overflow 'newname' */ | ||
2614 | if (strscpy(newname, p + 1, sizeof(newname)) <= 0) | ||
2615 | return NULL; | ||
2616 | *p = 0; | ||
2617 | /* Master must already exist */ | ||
2618 | if (!(n = isdn_net_findif(parm))) | ||
2619 | return NULL; | ||
2620 | /* Master must be a real interface, not a slave */ | ||
2621 | if (n->local->master) | ||
2622 | return NULL; | ||
2623 | /* Master must not be started yet */ | ||
2624 | if (isdn_net_device_started(n)) | ||
2625 | return NULL; | ||
2626 | return (isdn_net_new(newname, n->dev)); | ||
2627 | } | ||
2628 | return NULL; | ||
2629 | } | ||
2630 | |||
2631 | /* | ||
2632 | * Set interface-parameters. | ||
2633 | * Always set all parameters, so the user-level application is responsible | ||
2634 | * for not overwriting existing setups. It has to get the current | ||
2635 | * setup first, if only selected parameters are to be changed. | ||
2636 | */ | ||
2637 | int | ||
2638 | isdn_net_setcfg(isdn_net_ioctl_cfg *cfg) | ||
2639 | { | ||
2640 | isdn_net_dev *p = isdn_net_findif(cfg->name); | ||
2641 | ulong features; | ||
2642 | int i; | ||
2643 | int drvidx; | ||
2644 | int chidx; | ||
2645 | char drvid[25]; | ||
2646 | |||
2647 | if (p) { | ||
2648 | isdn_net_local *lp = p->local; | ||
2649 | |||
2650 | /* See if any registered driver supports the features we want */ | ||
2651 | features = ((1 << cfg->l2_proto) << ISDN_FEATURE_L2_SHIFT) | | ||
2652 | ((1 << cfg->l3_proto) << ISDN_FEATURE_L3_SHIFT); | ||
2653 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) | ||
2654 | if (dev->drv[i]) | ||
2655 | if ((dev->drv[i]->interface->features & features) == features) | ||
2656 | break; | ||
2657 | if (i == ISDN_MAX_DRIVERS) { | ||
2658 | printk(KERN_WARNING "isdn_net: No driver with selected features\n"); | ||
2659 | return -ENODEV; | ||
2660 | } | ||
2661 | if (lp->p_encap != cfg->p_encap) { | ||
2662 | #ifdef CONFIG_ISDN_X25 | ||
2663 | struct concap_proto *cprot = p->cprot; | ||
2664 | #endif | ||
2665 | if (isdn_net_device_started(p)) { | ||
2666 | printk(KERN_WARNING "%s: cannot change encap when if is up\n", | ||
2667 | p->dev->name); | ||
2668 | return -EBUSY; | ||
2669 | } | ||
2670 | #ifdef CONFIG_ISDN_X25 | ||
2671 | if (cprot && cprot->pops) | ||
2672 | cprot->pops->proto_del(cprot); | ||
2673 | p->cprot = NULL; | ||
2674 | lp->dops = NULL; | ||
2675 | /* ... , prepare for configuration of new one ... */ | ||
2676 | switch (cfg->p_encap) { | ||
2677 | case ISDN_NET_ENCAP_X25IFACE: | ||
2678 | lp->dops = &isdn_concap_reliable_dl_dops; | ||
2679 | } | ||
2680 | /* ... and allocate new one ... */ | ||
2681 | p->cprot = isdn_concap_new(cfg->p_encap); | ||
2682 | /* p -> cprot == NULL now if p_encap is not supported | ||
2683 | by means of the concap_proto mechanism */ | ||
2684 | /* the protocol is not configured yet; this will | ||
2685 | happen later when isdn_net_reset() is called */ | ||
2686 | #endif | ||
2687 | } | ||
2688 | switch (cfg->p_encap) { | ||
2689 | case ISDN_NET_ENCAP_SYNCPPP: | ||
2690 | #ifndef CONFIG_ISDN_PPP | ||
2691 | printk(KERN_WARNING "%s: SyncPPP support not configured\n", | ||
2692 | p->dev->name); | ||
2693 | return -EINVAL; | ||
2694 | #else | ||
2695 | p->dev->type = ARPHRD_PPP; /* change ARP type */ | ||
2696 | p->dev->addr_len = 0; | ||
2697 | #endif | ||
2698 | break; | ||
2699 | case ISDN_NET_ENCAP_X25IFACE: | ||
2700 | #ifndef CONFIG_ISDN_X25 | ||
2701 | printk(KERN_WARNING "%s: isdn-x25 support not configured\n", | ||
2702 | p->dev->name); | ||
2703 | return -EINVAL; | ||
2704 | #else | ||
2705 | p->dev->type = ARPHRD_X25; /* change ARP type */ | ||
2706 | p->dev->addr_len = 0; | ||
2707 | #endif | ||
2708 | break; | ||
2709 | case ISDN_NET_ENCAP_CISCOHDLCK: | ||
2710 | break; | ||
2711 | default: | ||
2712 | if (cfg->p_encap >= 0 && | ||
2713 | cfg->p_encap <= ISDN_NET_ENCAP_MAX_ENCAP) | ||
2714 | break; | ||
2715 | printk(KERN_WARNING | ||
2716 | "%s: encapsulation protocol %d not supported\n", | ||
2717 | p->dev->name, cfg->p_encap); | ||
2718 | return -EINVAL; | ||
2719 | } | ||
2720 | if (strlen(cfg->drvid)) { | ||
2721 | /* A bind has been requested ... */ | ||
2722 | char *c, | ||
2723 | *e; | ||
2724 | |||
2725 | if (strnlen(cfg->drvid, sizeof(cfg->drvid)) == | ||
2726 | sizeof(cfg->drvid)) | ||
2727 | return -EINVAL; | ||
2728 | drvidx = -1; | ||
2729 | chidx = -1; | ||
2730 | strcpy(drvid, cfg->drvid); | ||
2731 | if ((c = strchr(drvid, ','))) { | ||
2732 | /* The channel-number is appended to the driver-Id with a comma */ | ||
2733 | chidx = (int) simple_strtoul(c + 1, &e, 10); | ||
2734 | if (e == c) | ||
2735 | chidx = -1; | ||
2736 | *c = '\0'; | ||
2737 | } | ||
2738 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) | ||
2739 | /* Lookup driver-Id in array */ | ||
2740 | if (!(strcmp(dev->drvid[i], drvid))) { | ||
2741 | drvidx = i; | ||
2742 | break; | ||
2743 | } | ||
2744 | if ((drvidx == -1) || (chidx == -1)) | ||
2745 | /* Either driver-Id or channel-number invalid */ | ||
2746 | return -ENODEV; | ||
2747 | } else { | ||
2748 | /* Parameters are valid, so get them */ | ||
2749 | drvidx = lp->pre_device; | ||
2750 | chidx = lp->pre_channel; | ||
2751 | } | ||
2752 | if (cfg->exclusive > 0) { | ||
2753 | unsigned long flags; | ||
2754 | |||
2755 | /* If binding is exclusive, try to grab the channel */ | ||
2756 | spin_lock_irqsave(&dev->lock, flags); | ||
2757 | if ((i = isdn_get_free_channel(ISDN_USAGE_NET, | ||
2758 | lp->l2_proto, lp->l3_proto, drvidx, | ||
2759 | chidx, lp->msn)) < 0) { | ||
2760 | /* Grab failed, because desired channel is in use */ | ||
2761 | lp->exclusive = -1; | ||
2762 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2763 | return -EBUSY; | ||
2764 | } | ||
2765 | /* All went ok, so update isdninfo */ | ||
2766 | dev->usage[i] = ISDN_USAGE_EXCLUSIVE; | ||
2767 | isdn_info_update(); | ||
2768 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2769 | lp->exclusive = i; | ||
2770 | } else { | ||
2771 | /* Non-exclusive binding or unbind. */ | ||
2772 | lp->exclusive = -1; | ||
2773 | if ((lp->pre_device != -1) && (cfg->exclusive == -1)) { | ||
2774 | isdn_unexclusive_channel(lp->pre_device, lp->pre_channel); | ||
2775 | isdn_free_channel(lp->pre_device, lp->pre_channel, ISDN_USAGE_NET); | ||
2776 | drvidx = -1; | ||
2777 | chidx = -1; | ||
2778 | } | ||
2779 | } | ||
2780 | strlcpy(lp->msn, cfg->eaz, sizeof(lp->msn)); | ||
2781 | lp->pre_device = drvidx; | ||
2782 | lp->pre_channel = chidx; | ||
2783 | lp->onhtime = cfg->onhtime; | ||
2784 | lp->charge = cfg->charge; | ||
2785 | lp->l2_proto = cfg->l2_proto; | ||
2786 | lp->l3_proto = cfg->l3_proto; | ||
2787 | lp->cbdelay = cfg->cbdelay; | ||
2788 | lp->dialmax = cfg->dialmax; | ||
2789 | lp->triggercps = cfg->triggercps; | ||
2790 | lp->slavedelay = cfg->slavedelay * HZ; | ||
2791 | lp->pppbind = cfg->pppbind; | ||
2792 | lp->dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1; | ||
2793 | lp->dialwait = cfg->dialwait * HZ; | ||
2794 | if (cfg->secure) | ||
2795 | lp->flags |= ISDN_NET_SECURE; | ||
2796 | else | ||
2797 | lp->flags &= ~ISDN_NET_SECURE; | ||
2798 | if (cfg->cbhup) | ||
2799 | lp->flags |= ISDN_NET_CBHUP; | ||
2800 | else | ||
2801 | lp->flags &= ~ISDN_NET_CBHUP; | ||
2802 | switch (cfg->callback) { | ||
2803 | case 0: | ||
2804 | lp->flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT); | ||
2805 | break; | ||
2806 | case 1: | ||
2807 | lp->flags |= ISDN_NET_CALLBACK; | ||
2808 | lp->flags &= ~ISDN_NET_CBOUT; | ||
2809 | break; | ||
2810 | case 2: | ||
2811 | lp->flags |= ISDN_NET_CBOUT; | ||
2812 | lp->flags &= ~ISDN_NET_CALLBACK; | ||
2813 | break; | ||
2814 | } | ||
2815 | lp->flags &= ~ISDN_NET_DIALMODE_MASK; /* first all bits off */ | ||
2816 | if (cfg->dialmode && !(cfg->dialmode & ISDN_NET_DIALMODE_MASK)) { | ||
2817 | /* old isdnctrl version, where only 0 or 1 is given */ | ||
2818 | printk(KERN_WARNING | ||
2819 | "Old isdnctrl version detected! Please update.\n"); | ||
2820 | lp->flags |= ISDN_NET_DM_OFF; /* turn on `off' bit */ | ||
2821 | } | ||
2822 | else { | ||
2823 | lp->flags |= cfg->dialmode; /* turn on selected bits */ | ||
2824 | } | ||
2825 | if (cfg->chargehup) | ||
2826 | lp->hupflags |= ISDN_CHARGEHUP; | ||
2827 | else | ||
2828 | lp->hupflags &= ~ISDN_CHARGEHUP; | ||
2829 | if (cfg->ihup) | ||
2830 | lp->hupflags |= ISDN_INHUP; | ||
2831 | else | ||
2832 | lp->hupflags &= ~ISDN_INHUP; | ||
2833 | if (cfg->chargeint > 10) { | ||
2834 | lp->hupflags |= ISDN_CHARGEHUP | ISDN_HAVECHARGE | ISDN_MANCHARGE; | ||
2835 | lp->chargeint = cfg->chargeint * HZ; | ||
2836 | } | ||
2837 | if (cfg->p_encap != lp->p_encap) { | ||
2838 | if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) { | ||
2839 | p->dev->header_ops = NULL; | ||
2840 | p->dev->flags = IFF_NOARP | IFF_POINTOPOINT; | ||
2841 | } else { | ||
2842 | p->dev->header_ops = &isdn_header_ops; | ||
2843 | if (cfg->p_encap == ISDN_NET_ENCAP_ETHER) | ||
2844 | p->dev->flags = IFF_BROADCAST | IFF_MULTICAST; | ||
2845 | else | ||
2846 | p->dev->flags = IFF_NOARP | IFF_POINTOPOINT; | ||
2847 | } | ||
2848 | } | ||
2849 | lp->p_encap = cfg->p_encap; | ||
2850 | return 0; | ||
2851 | } | ||
2852 | return -ENODEV; | ||
2853 | } | ||
2854 | |||
2855 | /* | ||
2856 | * Perform get-interface-parameters.ioctl | ||
2857 | */ | ||
2858 | int | ||
2859 | isdn_net_getcfg(isdn_net_ioctl_cfg *cfg) | ||
2860 | { | ||
2861 | isdn_net_dev *p = isdn_net_findif(cfg->name); | ||
2862 | |||
2863 | if (p) { | ||
2864 | isdn_net_local *lp = p->local; | ||
2865 | |||
2866 | strcpy(cfg->eaz, lp->msn); | ||
2867 | cfg->exclusive = lp->exclusive; | ||
2868 | if (lp->pre_device >= 0) { | ||
2869 | sprintf(cfg->drvid, "%s,%d", dev->drvid[lp->pre_device], | ||
2870 | lp->pre_channel); | ||
2871 | } else | ||
2872 | cfg->drvid[0] = '\0'; | ||
2873 | cfg->onhtime = lp->onhtime; | ||
2874 | cfg->charge = lp->charge; | ||
2875 | cfg->l2_proto = lp->l2_proto; | ||
2876 | cfg->l3_proto = lp->l3_proto; | ||
2877 | cfg->p_encap = lp->p_encap; | ||
2878 | cfg->secure = (lp->flags & ISDN_NET_SECURE) ? 1 : 0; | ||
2879 | cfg->callback = 0; | ||
2880 | if (lp->flags & ISDN_NET_CALLBACK) | ||
2881 | cfg->callback = 1; | ||
2882 | if (lp->flags & ISDN_NET_CBOUT) | ||
2883 | cfg->callback = 2; | ||
2884 | cfg->cbhup = (lp->flags & ISDN_NET_CBHUP) ? 1 : 0; | ||
2885 | cfg->dialmode = lp->flags & ISDN_NET_DIALMODE_MASK; | ||
2886 | cfg->chargehup = (lp->hupflags & ISDN_CHARGEHUP) ? 1 : 0; | ||
2887 | cfg->ihup = (lp->hupflags & ISDN_INHUP) ? 1 : 0; | ||
2888 | cfg->cbdelay = lp->cbdelay; | ||
2889 | cfg->dialmax = lp->dialmax; | ||
2890 | cfg->triggercps = lp->triggercps; | ||
2891 | cfg->slavedelay = lp->slavedelay / HZ; | ||
2892 | cfg->chargeint = (lp->hupflags & ISDN_CHARGEHUP) ? | ||
2893 | (lp->chargeint / HZ) : 0; | ||
2894 | cfg->pppbind = lp->pppbind; | ||
2895 | cfg->dialtimeout = lp->dialtimeout >= 0 ? lp->dialtimeout / HZ : -1; | ||
2896 | cfg->dialwait = lp->dialwait / HZ; | ||
2897 | if (lp->slave) { | ||
2898 | if (strlen(lp->slave->name) >= 10) | ||
2899 | strcpy(cfg->slave, "too-long"); | ||
2900 | else | ||
2901 | strcpy(cfg->slave, lp->slave->name); | ||
2902 | } else | ||
2903 | cfg->slave[0] = '\0'; | ||
2904 | if (lp->master) { | ||
2905 | if (strlen(lp->master->name) >= 10) | ||
2906 | strcpy(cfg->master, "too-long"); | ||
2907 | else | ||
2908 | strcpy(cfg->master, lp->master->name); | ||
2909 | } else | ||
2910 | cfg->master[0] = '\0'; | ||
2911 | return 0; | ||
2912 | } | ||
2913 | return -ENODEV; | ||
2914 | } | ||
2915 | |||
2916 | /* | ||
2917 | * Add a phone-number to an interface. | ||
2918 | */ | ||
2919 | int | ||
2920 | isdn_net_addphone(isdn_net_ioctl_phone *phone) | ||
2921 | { | ||
2922 | isdn_net_dev *p = isdn_net_findif(phone->name); | ||
2923 | isdn_net_phone *n; | ||
2924 | |||
2925 | if (p) { | ||
2926 | if (!(n = kmalloc(sizeof(isdn_net_phone), GFP_KERNEL))) | ||
2927 | return -ENOMEM; | ||
2928 | strlcpy(n->num, phone->phone, sizeof(n->num)); | ||
2929 | n->next = p->local->phone[phone->outgoing & 1]; | ||
2930 | p->local->phone[phone->outgoing & 1] = n; | ||
2931 | return 0; | ||
2932 | } | ||
2933 | return -ENODEV; | ||
2934 | } | ||
2935 | |||
2936 | /* | ||
2937 | * Copy a string of all phone-numbers of an interface to user space. | ||
2938 | * This might sleep and must be called with the isdn semaphore down. | ||
2939 | */ | ||
2940 | int | ||
2941 | isdn_net_getphones(isdn_net_ioctl_phone *phone, char __user *phones) | ||
2942 | { | ||
2943 | isdn_net_dev *p = isdn_net_findif(phone->name); | ||
2944 | int inout = phone->outgoing & 1; | ||
2945 | int more = 0; | ||
2946 | int count = 0; | ||
2947 | isdn_net_phone *n; | ||
2948 | |||
2949 | if (!p) | ||
2950 | return -ENODEV; | ||
2951 | inout &= 1; | ||
2952 | for (n = p->local->phone[inout]; n; n = n->next) { | ||
2953 | if (more) { | ||
2954 | put_user(' ', phones++); | ||
2955 | count++; | ||
2956 | } | ||
2957 | if (copy_to_user(phones, n->num, strlen(n->num) + 1)) { | ||
2958 | return -EFAULT; | ||
2959 | } | ||
2960 | phones += strlen(n->num); | ||
2961 | count += strlen(n->num); | ||
2962 | more = 1; | ||
2963 | } | ||
2964 | put_user(0, phones); | ||
2965 | count++; | ||
2966 | return count; | ||
2967 | } | ||
2968 | |||
2969 | /* | ||
2970 | * Copy a string containing the peer's phone number of a connected interface | ||
2971 | * to user space. | ||
2972 | */ | ||
2973 | int | ||
2974 | isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone __user *peer) | ||
2975 | { | ||
2976 | isdn_net_dev *p = isdn_net_findif(phone->name); | ||
2977 | int ch, dv, idx; | ||
2978 | |||
2979 | if (!p) | ||
2980 | return -ENODEV; | ||
2981 | /* | ||
2982 | * Theoretical race: while this executes, the remote number might | ||
2983 | * become invalid (hang up) or change (new connection), resulting | ||
2984 | * in (partially) wrong number copied to user. This race | ||
2985 | * currently ignored. | ||
2986 | */ | ||
2987 | ch = p->local->isdn_channel; | ||
2988 | dv = p->local->isdn_device; | ||
2989 | if (ch < 0 && dv < 0) | ||
2990 | return -ENOTCONN; | ||
2991 | idx = isdn_dc2minor(dv, ch); | ||
2992 | if (idx < 0) | ||
2993 | return -ENODEV; | ||
2994 | /* for pre-bound channels, we need this extra check */ | ||
2995 | if (strncmp(dev->num[idx], "???", 3) == 0) | ||
2996 | return -ENOTCONN; | ||
2997 | strncpy(phone->phone, dev->num[idx], ISDN_MSNLEN); | ||
2998 | phone->outgoing = USG_OUTGOING(dev->usage[idx]); | ||
2999 | if (copy_to_user(peer, phone, sizeof(*peer))) | ||
3000 | return -EFAULT; | ||
3001 | return 0; | ||
3002 | } | ||
3003 | /* | ||
3004 | * Delete a phone-number from an interface. | ||
3005 | */ | ||
3006 | int | ||
3007 | isdn_net_delphone(isdn_net_ioctl_phone *phone) | ||
3008 | { | ||
3009 | isdn_net_dev *p = isdn_net_findif(phone->name); | ||
3010 | int inout = phone->outgoing & 1; | ||
3011 | isdn_net_phone *n; | ||
3012 | isdn_net_phone *m; | ||
3013 | |||
3014 | if (p) { | ||
3015 | n = p->local->phone[inout]; | ||
3016 | m = NULL; | ||
3017 | while (n) { | ||
3018 | if (!strcmp(n->num, phone->phone)) { | ||
3019 | if (p->local->dial == n) | ||
3020 | p->local->dial = n->next; | ||
3021 | if (m) | ||
3022 | m->next = n->next; | ||
3023 | else | ||
3024 | p->local->phone[inout] = n->next; | ||
3025 | kfree(n); | ||
3026 | return 0; | ||
3027 | } | ||
3028 | m = n; | ||
3029 | n = (isdn_net_phone *) n->next; | ||
3030 | } | ||
3031 | return -EINVAL; | ||
3032 | } | ||
3033 | return -ENODEV; | ||
3034 | } | ||
3035 | |||
3036 | /* | ||
3037 | * Delete all phone-numbers of an interface. | ||
3038 | */ | ||
3039 | static int | ||
3040 | isdn_net_rmallphone(isdn_net_dev *p) | ||
3041 | { | ||
3042 | isdn_net_phone *n; | ||
3043 | isdn_net_phone *m; | ||
3044 | int i; | ||
3045 | |||
3046 | for (i = 0; i < 2; i++) { | ||
3047 | n = p->local->phone[i]; | ||
3048 | while (n) { | ||
3049 | m = n->next; | ||
3050 | kfree(n); | ||
3051 | n = m; | ||
3052 | } | ||
3053 | p->local->phone[i] = NULL; | ||
3054 | } | ||
3055 | p->local->dial = NULL; | ||
3056 | return 0; | ||
3057 | } | ||
3058 | |||
3059 | /* | ||
3060 | * Force a hangup of a network-interface. | ||
3061 | */ | ||
3062 | int | ||
3063 | isdn_net_force_hangup(char *name) | ||
3064 | { | ||
3065 | isdn_net_dev *p = isdn_net_findif(name); | ||
3066 | struct net_device *q; | ||
3067 | |||
3068 | if (p) { | ||
3069 | if (p->local->isdn_device < 0) | ||
3070 | return 1; | ||
3071 | q = p->local->slave; | ||
3072 | /* If this interface has slaves, do a hangup for them also. */ | ||
3073 | while (q) { | ||
3074 | isdn_net_hangup(q); | ||
3075 | q = MASTER_TO_SLAVE(q); | ||
3076 | } | ||
3077 | isdn_net_hangup(p->dev); | ||
3078 | return 0; | ||
3079 | } | ||
3080 | return -ENODEV; | ||
3081 | } | ||
3082 | |||
3083 | /* | ||
3084 | * Helper-function for isdn_net_rm: Do the real work. | ||
3085 | */ | ||
3086 | static int | ||
3087 | isdn_net_realrm(isdn_net_dev *p, isdn_net_dev *q) | ||
3088 | { | ||
3089 | u_long flags; | ||
3090 | |||
3091 | if (isdn_net_device_started(p)) { | ||
3092 | return -EBUSY; | ||
3093 | } | ||
3094 | #ifdef CONFIG_ISDN_X25 | ||
3095 | if (p->cprot && p->cprot->pops) | ||
3096 | p->cprot->pops->proto_del(p->cprot); | ||
3097 | #endif | ||
3098 | /* Free all phone-entries */ | ||
3099 | isdn_net_rmallphone(p); | ||
3100 | /* If interface is bound exclusive, free channel-usage */ | ||
3101 | if (p->local->exclusive != -1) | ||
3102 | isdn_unexclusive_channel(p->local->pre_device, p->local->pre_channel); | ||
3103 | if (p->local->master) { | ||
3104 | /* It's a slave-device, so update master's slave-pointer if necessary */ | ||
3105 | if (((isdn_net_local *) ISDN_MASTER_PRIV(p->local))->slave == | ||
3106 | p->dev) | ||
3107 | ((isdn_net_local *)ISDN_MASTER_PRIV(p->local))->slave = | ||
3108 | p->local->slave; | ||
3109 | } else { | ||
3110 | /* Unregister only if it's a master-device */ | ||
3111 | unregister_netdev(p->dev); | ||
3112 | } | ||
3113 | /* Unlink device from chain */ | ||
3114 | spin_lock_irqsave(&dev->lock, flags); | ||
3115 | if (q) | ||
3116 | q->next = p->next; | ||
3117 | else | ||
3118 | dev->netdev = p->next; | ||
3119 | if (p->local->slave) { | ||
3120 | /* If this interface has a slave, remove it also */ | ||
3121 | char *slavename = p->local->slave->name; | ||
3122 | isdn_net_dev *n = dev->netdev; | ||
3123 | q = NULL; | ||
3124 | while (n) { | ||
3125 | if (!strcmp(n->dev->name, slavename)) { | ||
3126 | spin_unlock_irqrestore(&dev->lock, flags); | ||
3127 | isdn_net_realrm(n, q); | ||
3128 | spin_lock_irqsave(&dev->lock, flags); | ||
3129 | break; | ||
3130 | } | ||
3131 | q = n; | ||
3132 | n = (isdn_net_dev *)n->next; | ||
3133 | } | ||
3134 | } | ||
3135 | spin_unlock_irqrestore(&dev->lock, flags); | ||
3136 | /* If no more net-devices remain, disable auto-hangup timer */ | ||
3137 | if (dev->netdev == NULL) | ||
3138 | isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0); | ||
3139 | free_netdev(p->dev); | ||
3140 | kfree(p); | ||
3141 | |||
3142 | return 0; | ||
3143 | } | ||
3144 | |||
3145 | /* | ||
3146 | * Remove a single network-interface. | ||
3147 | */ | ||
3148 | int | ||
3149 | isdn_net_rm(char *name) | ||
3150 | { | ||
3151 | u_long flags; | ||
3152 | isdn_net_dev *p; | ||
3153 | isdn_net_dev *q; | ||
3154 | |||
3155 | /* Search name in netdev-chain */ | ||
3156 | spin_lock_irqsave(&dev->lock, flags); | ||
3157 | p = dev->netdev; | ||
3158 | q = NULL; | ||
3159 | while (p) { | ||
3160 | if (!strcmp(p->dev->name, name)) { | ||
3161 | spin_unlock_irqrestore(&dev->lock, flags); | ||
3162 | return (isdn_net_realrm(p, q)); | ||
3163 | } | ||
3164 | q = p; | ||
3165 | p = (isdn_net_dev *) p->next; | ||
3166 | } | ||
3167 | spin_unlock_irqrestore(&dev->lock, flags); | ||
3168 | /* If no more net-devices remain, disable auto-hangup timer */ | ||
3169 | if (dev->netdev == NULL) | ||
3170 | isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0); | ||
3171 | return -ENODEV; | ||
3172 | } | ||
3173 | |||
3174 | /* | ||
3175 | * Remove all network-interfaces | ||
3176 | */ | ||
3177 | int | ||
3178 | isdn_net_rmall(void) | ||
3179 | { | ||
3180 | u_long flags; | ||
3181 | int ret; | ||
3182 | |||
3183 | /* Walk through netdev-chain */ | ||
3184 | spin_lock_irqsave(&dev->lock, flags); | ||
3185 | while (dev->netdev) { | ||
3186 | if (!dev->netdev->local->master) { | ||
3187 | /* Remove master-devices only, slaves get removed with their master */ | ||
3188 | spin_unlock_irqrestore(&dev->lock, flags); | ||
3189 | if ((ret = isdn_net_realrm(dev->netdev, NULL))) { | ||
3190 | return ret; | ||
3191 | } | ||
3192 | spin_lock_irqsave(&dev->lock, flags); | ||
3193 | } | ||
3194 | } | ||
3195 | dev->netdev = NULL; | ||
3196 | spin_unlock_irqrestore(&dev->lock, flags); | ||
3197 | return 0; | ||
3198 | } | ||
diff --git a/drivers/isdn/i4l/isdn_net.h b/drivers/isdn/i4l/isdn_net.h deleted file mode 100644 index cca6d68da171..000000000000 --- a/drivers/isdn/i4l/isdn_net.h +++ /dev/null | |||
@@ -1,151 +0,0 @@ | |||
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 | #define ISDN_MASTER_PRIV(lp) ((isdn_net_local *) netdev_priv(lp->master)) | ||
60 | #define ISDN_SLAVE_PRIV(lp) ((isdn_net_local *) netdev_priv(lp->slave)) | ||
61 | #define MASTER_TO_SLAVE(master) \ | ||
62 | (((isdn_net_local *) netdev_priv(master))->slave) | ||
63 | |||
64 | /* | ||
65 | * is this particular channel busy? | ||
66 | */ | ||
67 | static __inline__ int isdn_net_lp_busy(isdn_net_local *lp) | ||
68 | { | ||
69 | if (atomic_read(&lp->frame_cnt) < ISDN_NET_MAX_QUEUE_LENGTH) | ||
70 | return 0; | ||
71 | else | ||
72 | return 1; | ||
73 | } | ||
74 | |||
75 | /* | ||
76 | * For the given net device, this will get a non-busy channel out of the | ||
77 | * corresponding bundle. The returned channel is locked. | ||
78 | */ | ||
79 | static __inline__ isdn_net_local *isdn_net_get_locked_lp(isdn_net_dev *nd) | ||
80 | { | ||
81 | unsigned long flags; | ||
82 | isdn_net_local *lp; | ||
83 | |||
84 | spin_lock_irqsave(&nd->queue_lock, flags); | ||
85 | lp = nd->queue; /* get lp on top of queue */ | ||
86 | while (isdn_net_lp_busy(nd->queue)) { | ||
87 | nd->queue = nd->queue->next; | ||
88 | if (nd->queue == lp) { /* not found -- should never happen */ | ||
89 | lp = NULL; | ||
90 | goto errout; | ||
91 | } | ||
92 | } | ||
93 | lp = nd->queue; | ||
94 | nd->queue = nd->queue->next; | ||
95 | spin_unlock_irqrestore(&nd->queue_lock, flags); | ||
96 | spin_lock(&lp->xmit_lock); | ||
97 | local_bh_disable(); | ||
98 | return lp; | ||
99 | errout: | ||
100 | spin_unlock_irqrestore(&nd->queue_lock, flags); | ||
101 | return lp; | ||
102 | } | ||
103 | |||
104 | /* | ||
105 | * add a channel to a bundle | ||
106 | */ | ||
107 | static __inline__ void isdn_net_add_to_bundle(isdn_net_dev *nd, isdn_net_local *nlp) | ||
108 | { | ||
109 | isdn_net_local *lp; | ||
110 | unsigned long flags; | ||
111 | |||
112 | spin_lock_irqsave(&nd->queue_lock, flags); | ||
113 | |||
114 | lp = nd->queue; | ||
115 | // printk(KERN_DEBUG "%s: lp:%s(%p) nlp:%s(%p) last(%p)\n", | ||
116 | // __func__, lp->name, lp, nlp->name, nlp, lp->last); | ||
117 | nlp->last = lp->last; | ||
118 | lp->last->next = nlp; | ||
119 | lp->last = nlp; | ||
120 | nlp->next = lp; | ||
121 | nd->queue = nlp; | ||
122 | |||
123 | spin_unlock_irqrestore(&nd->queue_lock, flags); | ||
124 | } | ||
125 | /* | ||
126 | * remove a channel from the bundle it belongs to | ||
127 | */ | ||
128 | static __inline__ void isdn_net_rm_from_bundle(isdn_net_local *lp) | ||
129 | { | ||
130 | isdn_net_local *master_lp = lp; | ||
131 | unsigned long flags; | ||
132 | |||
133 | if (lp->master) | ||
134 | master_lp = ISDN_MASTER_PRIV(lp); | ||
135 | |||
136 | // printk(KERN_DEBUG "%s: lp:%s(%p) mlp:%s(%p) last(%p) next(%p) mndq(%p)\n", | ||
137 | // __func__, lp->name, lp, master_lp->name, master_lp, lp->last, lp->next, master_lp->netdev->queue); | ||
138 | spin_lock_irqsave(&master_lp->netdev->queue_lock, flags); | ||
139 | lp->last->next = lp->next; | ||
140 | lp->next->last = lp->last; | ||
141 | if (master_lp->netdev->queue == lp) { | ||
142 | master_lp->netdev->queue = lp->next; | ||
143 | if (lp->next == lp) { /* last in queue */ | ||
144 | master_lp->netdev->queue = master_lp->netdev->local; | ||
145 | } | ||
146 | } | ||
147 | lp->next = lp->last = lp; /* (re)set own pointers */ | ||
148 | // printk(KERN_DEBUG "%s: mndq(%p)\n", | ||
149 | // __func__, master_lp->netdev->queue); | ||
150 | spin_unlock_irqrestore(&master_lp->netdev->queue_lock, flags); | ||
151 | } | ||
diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c deleted file mode 100644 index 7e0f419c14f8..000000000000 --- a/drivers/isdn/i4l/isdn_ppp.c +++ /dev/null | |||
@@ -1,3046 +0,0 @@ | |||
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/isdn.h> | ||
13 | #include <linux/poll.h> | ||
14 | #include <linux/ppp-comp.h> | ||
15 | #include <linux/slab.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(struct timer_list *t); | ||
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 | __func__, 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 | __func__, 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 | /* get unit number from interface name .. ugly! */ | ||
195 | unit = isdn_ppp_if_get_unit(lp->netdev->dev->name); | ||
196 | if (unit < 0) { | ||
197 | printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n", | ||
198 | lp->netdev->dev->name); | ||
199 | retval = -1; | ||
200 | goto out; | ||
201 | } | ||
202 | |||
203 | lp->ppp_slot = i; | ||
204 | is = ippp_table[i]; | ||
205 | is->lp = lp; | ||
206 | is->unit = unit; | ||
207 | is->state = IPPP_OPEN | IPPP_ASSIGNED; /* assigned to a netdevice but not connected */ | ||
208 | #ifdef CONFIG_ISDN_MPP | ||
209 | retval = isdn_ppp_mp_init(lp, NULL); | ||
210 | if (retval < 0) | ||
211 | goto out; | ||
212 | #endif /* CONFIG_ISDN_MPP */ | ||
213 | |||
214 | retval = lp->ppp_slot; | ||
215 | |||
216 | out: | ||
217 | return retval; | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * kick the ipppd on the device | ||
222 | * (wakes up daemon after B-channel connect) | ||
223 | */ | ||
224 | |||
225 | void | ||
226 | isdn_ppp_wakeup_daemon(isdn_net_local *lp) | ||
227 | { | ||
228 | if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) { | ||
229 | printk(KERN_ERR "%s: ppp_slot(%d) out of range\n", | ||
230 | __func__, lp->ppp_slot); | ||
231 | return; | ||
232 | } | ||
233 | ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK; | ||
234 | wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq); | ||
235 | } | ||
236 | |||
237 | /* | ||
238 | * there was a hangup on the netdevice | ||
239 | * force wakeup of the ippp device | ||
240 | * go into 'device waits for release' state | ||
241 | */ | ||
242 | static int | ||
243 | isdn_ppp_closewait(int slot) | ||
244 | { | ||
245 | struct ippp_struct *is; | ||
246 | |||
247 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
248 | printk(KERN_ERR "%s: slot(%d) out of range\n", | ||
249 | __func__, slot); | ||
250 | return 0; | ||
251 | } | ||
252 | is = ippp_table[slot]; | ||
253 | if (is->state) | ||
254 | wake_up_interruptible(&is->wq); | ||
255 | is->state = IPPP_CLOSEWAIT; | ||
256 | return 1; | ||
257 | } | ||
258 | |||
259 | /* | ||
260 | * isdn_ppp_find_slot / isdn_ppp_free_slot | ||
261 | */ | ||
262 | |||
263 | static int | ||
264 | isdn_ppp_get_slot(void) | ||
265 | { | ||
266 | int i; | ||
267 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
268 | if (!ippp_table[i]->state) | ||
269 | return i; | ||
270 | } | ||
271 | return -1; | ||
272 | } | ||
273 | |||
274 | /* | ||
275 | * isdn_ppp_open | ||
276 | */ | ||
277 | |||
278 | int | ||
279 | isdn_ppp_open(int min, struct file *file) | ||
280 | { | ||
281 | int slot; | ||
282 | struct ippp_struct *is; | ||
283 | |||
284 | if (min < 0 || min >= ISDN_MAX_CHANNELS) | ||
285 | return -ENODEV; | ||
286 | |||
287 | slot = isdn_ppp_get_slot(); | ||
288 | if (slot < 0) { | ||
289 | return -EBUSY; | ||
290 | } | ||
291 | is = file->private_data = ippp_table[slot]; | ||
292 | |||
293 | printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n", | ||
294 | slot, min, is->state); | ||
295 | |||
296 | /* compression stuff */ | ||
297 | is->link_compressor = is->compressor = NULL; | ||
298 | is->link_decompressor = is->decompressor = NULL; | ||
299 | is->link_comp_stat = is->comp_stat = NULL; | ||
300 | is->link_decomp_stat = is->decomp_stat = NULL; | ||
301 | is->compflags = 0; | ||
302 | |||
303 | is->reset = isdn_ppp_ccp_reset_alloc(is); | ||
304 | if (!is->reset) | ||
305 | return -ENOMEM; | ||
306 | |||
307 | is->lp = NULL; | ||
308 | is->mp_seqno = 0; /* MP sequence number */ | ||
309 | is->pppcfg = 0; /* ppp configuration */ | ||
310 | is->mpppcfg = 0; /* mppp configuration */ | ||
311 | is->last_link_seqno = -1; /* MP: maybe set to Bundle-MIN, when joining a bundle ?? */ | ||
312 | is->unit = -1; /* set, when we have our interface */ | ||
313 | is->mru = 1524; /* MRU, default 1524 */ | ||
314 | is->maxcid = 16; /* VJ: maxcid */ | ||
315 | is->tk = current; | ||
316 | init_waitqueue_head(&is->wq); | ||
317 | is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */ | ||
318 | is->last = is->rq; | ||
319 | is->minor = min; | ||
320 | #ifdef CONFIG_ISDN_PPP_VJ | ||
321 | /* | ||
322 | * VJ header compression init | ||
323 | */ | ||
324 | is->slcomp = slhc_init(16, 16); /* not necessary for 2. link in bundle */ | ||
325 | if (IS_ERR(is->slcomp)) { | ||
326 | isdn_ppp_ccp_reset_free(is); | ||
327 | return PTR_ERR(is->slcomp); | ||
328 | } | ||
329 | #endif | ||
330 | #ifdef CONFIG_IPPP_FILTER | ||
331 | is->pass_filter = NULL; | ||
332 | is->active_filter = NULL; | ||
333 | #endif | ||
334 | is->state = IPPP_OPEN; | ||
335 | |||
336 | return 0; | ||
337 | } | ||
338 | |||
339 | /* | ||
340 | * release ippp device | ||
341 | */ | ||
342 | void | ||
343 | isdn_ppp_release(int min, struct file *file) | ||
344 | { | ||
345 | int i; | ||
346 | struct ippp_struct *is; | ||
347 | |||
348 | if (min < 0 || min >= ISDN_MAX_CHANNELS) | ||
349 | return; | ||
350 | is = file->private_data; | ||
351 | |||
352 | if (!is) { | ||
353 | printk(KERN_ERR "%s: no file->private_data\n", __func__); | ||
354 | return; | ||
355 | } | ||
356 | if (is->debug & 0x1) | ||
357 | printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", min, (long) is->lp); | ||
358 | |||
359 | if (is->lp) { /* a lp address says: this link is still up */ | ||
360 | isdn_net_dev *p = is->lp->netdev; | ||
361 | |||
362 | if (!p) { | ||
363 | printk(KERN_ERR "%s: no lp->netdev\n", __func__); | ||
364 | return; | ||
365 | } | ||
366 | is->state &= ~IPPP_CONNECT; /* -> effect: no call of wakeup */ | ||
367 | /* | ||
368 | * isdn_net_hangup() calls isdn_ppp_free() | ||
369 | * isdn_ppp_free() sets is->lp to NULL and lp->ppp_slot to -1 | ||
370 | * removing the IPPP_CONNECT flag omits calling of isdn_ppp_wakeup_daemon() | ||
371 | */ | ||
372 | isdn_net_hangup(p->dev); | ||
373 | } | ||
374 | for (i = 0; i < NUM_RCV_BUFFS; i++) { | ||
375 | kfree(is->rq[i].buf); | ||
376 | is->rq[i].buf = NULL; | ||
377 | } | ||
378 | is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */ | ||
379 | is->last = is->rq; | ||
380 | |||
381 | #ifdef CONFIG_ISDN_PPP_VJ | ||
382 | /* TODO: if this was the previous master: link the slcomp to the new master */ | ||
383 | slhc_free(is->slcomp); | ||
384 | is->slcomp = NULL; | ||
385 | #endif | ||
386 | #ifdef CONFIG_IPPP_FILTER | ||
387 | if (is->pass_filter) { | ||
388 | bpf_prog_destroy(is->pass_filter); | ||
389 | is->pass_filter = NULL; | ||
390 | } | ||
391 | |||
392 | if (is->active_filter) { | ||
393 | bpf_prog_destroy(is->active_filter); | ||
394 | is->active_filter = NULL; | ||
395 | } | ||
396 | #endif | ||
397 | |||
398 | /* TODO: if this was the previous master: link the stuff to the new master */ | ||
399 | if (is->comp_stat) | ||
400 | is->compressor->free(is->comp_stat); | ||
401 | if (is->link_comp_stat) | ||
402 | is->link_compressor->free(is->link_comp_stat); | ||
403 | if (is->link_decomp_stat) | ||
404 | is->link_decompressor->free(is->link_decomp_stat); | ||
405 | if (is->decomp_stat) | ||
406 | is->decompressor->free(is->decomp_stat); | ||
407 | is->compressor = is->link_compressor = NULL; | ||
408 | is->decompressor = is->link_decompressor = NULL; | ||
409 | is->comp_stat = is->link_comp_stat = NULL; | ||
410 | is->decomp_stat = is->link_decomp_stat = NULL; | ||
411 | |||
412 | /* Clean up if necessary */ | ||
413 | if (is->reset) | ||
414 | isdn_ppp_ccp_reset_free(is); | ||
415 | |||
416 | /* this slot is ready for new connections */ | ||
417 | is->state = 0; | ||
418 | } | ||
419 | |||
420 | /* | ||
421 | * get_arg .. ioctl helper | ||
422 | */ | ||
423 | static int | ||
424 | get_arg(void __user *b, void *val, int len) | ||
425 | { | ||
426 | if (len <= 0) | ||
427 | len = sizeof(void *); | ||
428 | if (copy_from_user(val, b, len)) | ||
429 | return -EFAULT; | ||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | /* | ||
434 | * set arg .. ioctl helper | ||
435 | */ | ||
436 | static int | ||
437 | set_arg(void __user *b, void *val, int len) | ||
438 | { | ||
439 | if (len <= 0) | ||
440 | len = sizeof(void *); | ||
441 | if (copy_to_user(b, val, len)) | ||
442 | return -EFAULT; | ||
443 | return 0; | ||
444 | } | ||
445 | |||
446 | #ifdef CONFIG_IPPP_FILTER | ||
447 | static int get_filter(void __user *arg, struct sock_filter **p) | ||
448 | { | ||
449 | struct sock_fprog uprog; | ||
450 | struct sock_filter *code = NULL; | ||
451 | int len; | ||
452 | |||
453 | if (copy_from_user(&uprog, arg, sizeof(uprog))) | ||
454 | return -EFAULT; | ||
455 | |||
456 | if (!uprog.len) { | ||
457 | *p = NULL; | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | /* uprog.len is unsigned short, so no overflow here */ | ||
462 | len = uprog.len * sizeof(struct sock_filter); | ||
463 | code = memdup_user(uprog.filter, len); | ||
464 | if (IS_ERR(code)) | ||
465 | return PTR_ERR(code); | ||
466 | |||
467 | *p = code; | ||
468 | return uprog.len; | ||
469 | } | ||
470 | #endif /* CONFIG_IPPP_FILTER */ | ||
471 | |||
472 | /* | ||
473 | * ippp device ioctl | ||
474 | */ | ||
475 | int | ||
476 | isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg) | ||
477 | { | ||
478 | unsigned long val; | ||
479 | int r, i, j; | ||
480 | struct ippp_struct *is; | ||
481 | isdn_net_local *lp; | ||
482 | struct isdn_ppp_comp_data data; | ||
483 | void __user *argp = (void __user *)arg; | ||
484 | |||
485 | is = file->private_data; | ||
486 | lp = is->lp; | ||
487 | |||
488 | if (is->debug & 0x1) | ||
489 | printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n", min, cmd, is->state); | ||
490 | |||
491 | if (!(is->state & IPPP_OPEN)) | ||
492 | return -EINVAL; | ||
493 | |||
494 | switch (cmd) { | ||
495 | case PPPIOCBUNDLE: | ||
496 | #ifdef CONFIG_ISDN_MPP | ||
497 | if (!(is->state & IPPP_CONNECT)) | ||
498 | return -EINVAL; | ||
499 | if ((r = get_arg(argp, &val, sizeof(val)))) | ||
500 | return r; | ||
501 | printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n", | ||
502 | (int) min, (int) is->unit, (int) val); | ||
503 | return isdn_ppp_bundle(is, val); | ||
504 | #else | ||
505 | return -1; | ||
506 | #endif | ||
507 | break; | ||
508 | case PPPIOCGUNIT: /* get ppp/isdn unit number */ | ||
509 | if ((r = set_arg(argp, &is->unit, sizeof(is->unit)))) | ||
510 | return r; | ||
511 | break; | ||
512 | case PPPIOCGIFNAME: | ||
513 | if (!lp) | ||
514 | return -EINVAL; | ||
515 | if ((r = set_arg(argp, lp->netdev->dev->name, | ||
516 | strlen(lp->netdev->dev->name)))) | ||
517 | return r; | ||
518 | break; | ||
519 | case PPPIOCGMPFLAGS: /* get configuration flags */ | ||
520 | if ((r = set_arg(argp, &is->mpppcfg, sizeof(is->mpppcfg)))) | ||
521 | return r; | ||
522 | break; | ||
523 | case PPPIOCSMPFLAGS: /* set configuration flags */ | ||
524 | if ((r = get_arg(argp, &val, sizeof(val)))) | ||
525 | return r; | ||
526 | is->mpppcfg = val; | ||
527 | break; | ||
528 | case PPPIOCGFLAGS: /* get configuration flags */ | ||
529 | if ((r = set_arg(argp, &is->pppcfg, sizeof(is->pppcfg)))) | ||
530 | return r; | ||
531 | break; | ||
532 | case PPPIOCSFLAGS: /* set configuration flags */ | ||
533 | if ((r = get_arg(argp, &val, sizeof(val)))) { | ||
534 | return r; | ||
535 | } | ||
536 | if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT)) { | ||
537 | if (lp) { | ||
538 | /* OK .. we are ready to send buffers */ | ||
539 | is->pppcfg = val; /* isdn_ppp_xmit test for SC_ENABLE_IP !!! */ | ||
540 | netif_wake_queue(lp->netdev->dev); | ||
541 | break; | ||
542 | } | ||
543 | } | ||
544 | is->pppcfg = val; | ||
545 | break; | ||
546 | case PPPIOCGIDLE: /* get idle time information */ | ||
547 | if (lp) { | ||
548 | struct ppp_idle pidle; | ||
549 | pidle.xmit_idle = pidle.recv_idle = lp->huptimer; | ||
550 | if ((r = set_arg(argp, &pidle, sizeof(struct ppp_idle)))) | ||
551 | return r; | ||
552 | } | ||
553 | break; | ||
554 | case PPPIOCSMRU: /* set receive unit size for PPP */ | ||
555 | if ((r = get_arg(argp, &val, sizeof(val)))) | ||
556 | return r; | ||
557 | is->mru = val; | ||
558 | break; | ||
559 | case PPPIOCSMPMRU: | ||
560 | break; | ||
561 | case PPPIOCSMPMTU: | ||
562 | break; | ||
563 | case PPPIOCSMAXCID: /* set the maximum compression slot id */ | ||
564 | if ((r = get_arg(argp, &val, sizeof(val)))) | ||
565 | return r; | ||
566 | val++; | ||
567 | if (is->maxcid != val) { | ||
568 | #ifdef CONFIG_ISDN_PPP_VJ | ||
569 | struct slcompress *sltmp; | ||
570 | #endif | ||
571 | if (is->debug & 0x1) | ||
572 | printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n", val); | ||
573 | is->maxcid = val; | ||
574 | #ifdef CONFIG_ISDN_PPP_VJ | ||
575 | sltmp = slhc_init(16, val); | ||
576 | if (IS_ERR(sltmp)) | ||
577 | return PTR_ERR(sltmp); | ||
578 | if (is->slcomp) | ||
579 | slhc_free(is->slcomp); | ||
580 | is->slcomp = sltmp; | ||
581 | #endif | ||
582 | } | ||
583 | break; | ||
584 | case PPPIOCGDEBUG: | ||
585 | if ((r = set_arg(argp, &is->debug, sizeof(is->debug)))) | ||
586 | return r; | ||
587 | break; | ||
588 | case PPPIOCSDEBUG: | ||
589 | if ((r = get_arg(argp, &val, sizeof(val)))) | ||
590 | return r; | ||
591 | is->debug = val; | ||
592 | break; | ||
593 | case PPPIOCGCOMPRESSORS: | ||
594 | { | ||
595 | unsigned long protos[8] = {0,}; | ||
596 | struct isdn_ppp_compressor *ipc = ipc_head; | ||
597 | while (ipc) { | ||
598 | j = ipc->num / (sizeof(long) * 8); | ||
599 | i = ipc->num % (sizeof(long) * 8); | ||
600 | if (j < 8) | ||
601 | protos[j] |= (1UL << i); | ||
602 | ipc = ipc->next; | ||
603 | } | ||
604 | if ((r = set_arg(argp, protos, 8 * sizeof(long)))) | ||
605 | return r; | ||
606 | } | ||
607 | break; | ||
608 | case PPPIOCSCOMPRESSOR: | ||
609 | if ((r = get_arg(argp, &data, sizeof(struct isdn_ppp_comp_data)))) | ||
610 | return r; | ||
611 | return isdn_ppp_set_compressor(is, &data); | ||
612 | case PPPIOCGCALLINFO: | ||
613 | { | ||
614 | struct pppcallinfo pci; | ||
615 | memset((char *)&pci, 0, sizeof(struct pppcallinfo)); | ||
616 | if (lp) | ||
617 | { | ||
618 | strncpy(pci.local_num, lp->msn, 63); | ||
619 | if (lp->dial) { | ||
620 | strncpy(pci.remote_num, lp->dial->num, 63); | ||
621 | } | ||
622 | pci.charge_units = lp->charge; | ||
623 | if (lp->outgoing) | ||
624 | pci.calltype = CALLTYPE_OUTGOING; | ||
625 | else | ||
626 | pci.calltype = CALLTYPE_INCOMING; | ||
627 | if (lp->flags & ISDN_NET_CALLBACK) | ||
628 | pci.calltype |= CALLTYPE_CALLBACK; | ||
629 | } | ||
630 | return set_arg(argp, &pci, sizeof(struct pppcallinfo)); | ||
631 | } | ||
632 | #ifdef CONFIG_IPPP_FILTER | ||
633 | case PPPIOCSPASS: | ||
634 | { | ||
635 | struct sock_fprog_kern fprog; | ||
636 | struct sock_filter *code; | ||
637 | int err, len = get_filter(argp, &code); | ||
638 | |||
639 | if (len < 0) | ||
640 | return len; | ||
641 | |||
642 | fprog.len = len; | ||
643 | fprog.filter = code; | ||
644 | |||
645 | if (is->pass_filter) { | ||
646 | bpf_prog_destroy(is->pass_filter); | ||
647 | is->pass_filter = NULL; | ||
648 | } | ||
649 | if (fprog.filter != NULL) | ||
650 | err = bpf_prog_create(&is->pass_filter, &fprog); | ||
651 | else | ||
652 | err = 0; | ||
653 | kfree(code); | ||
654 | |||
655 | return err; | ||
656 | } | ||
657 | case PPPIOCSACTIVE: | ||
658 | { | ||
659 | struct sock_fprog_kern fprog; | ||
660 | struct sock_filter *code; | ||
661 | int err, len = get_filter(argp, &code); | ||
662 | |||
663 | if (len < 0) | ||
664 | return len; | ||
665 | |||
666 | fprog.len = len; | ||
667 | fprog.filter = code; | ||
668 | |||
669 | if (is->active_filter) { | ||
670 | bpf_prog_destroy(is->active_filter); | ||
671 | is->active_filter = NULL; | ||
672 | } | ||
673 | if (fprog.filter != NULL) | ||
674 | err = bpf_prog_create(&is->active_filter, &fprog); | ||
675 | else | ||
676 | err = 0; | ||
677 | kfree(code); | ||
678 | |||
679 | return err; | ||
680 | } | ||
681 | #endif /* CONFIG_IPPP_FILTER */ | ||
682 | default: | ||
683 | break; | ||
684 | } | ||
685 | return 0; | ||
686 | } | ||
687 | |||
688 | __poll_t | ||
689 | isdn_ppp_poll(struct file *file, poll_table *wait) | ||
690 | { | ||
691 | __poll_t mask; | ||
692 | struct ippp_buf_queue *bf, *bl; | ||
693 | u_long flags; | ||
694 | struct ippp_struct *is; | ||
695 | |||
696 | is = file->private_data; | ||
697 | |||
698 | if (is->debug & 0x2) | ||
699 | printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n", | ||
700 | iminor(file_inode(file))); | ||
701 | |||
702 | /* just registers wait_queue hook. This doesn't really wait. */ | ||
703 | poll_wait(file, &is->wq, wait); | ||
704 | |||
705 | if (!(is->state & IPPP_OPEN)) { | ||
706 | if (is->state == IPPP_CLOSEWAIT) | ||
707 | return EPOLLHUP; | ||
708 | printk(KERN_DEBUG "isdn_ppp: device not open\n"); | ||
709 | return EPOLLERR; | ||
710 | } | ||
711 | /* we're always ready to send .. */ | ||
712 | mask = EPOLLOUT | EPOLLWRNORM; | ||
713 | |||
714 | spin_lock_irqsave(&is->buflock, flags); | ||
715 | bl = is->last; | ||
716 | bf = is->first; | ||
717 | /* | ||
718 | * if IPPP_NOBLOCK is set we return even if we have nothing to read | ||
719 | */ | ||
720 | if (bf->next != bl || (is->state & IPPP_NOBLOCK)) { | ||
721 | is->state &= ~IPPP_NOBLOCK; | ||
722 | mask |= EPOLLIN | EPOLLRDNORM; | ||
723 | } | ||
724 | spin_unlock_irqrestore(&is->buflock, flags); | ||
725 | return mask; | ||
726 | } | ||
727 | |||
728 | /* | ||
729 | * fill up isdn_ppp_read() queue .. | ||
730 | */ | ||
731 | |||
732 | static int | ||
733 | isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot) | ||
734 | { | ||
735 | struct ippp_buf_queue *bf, *bl; | ||
736 | u_long flags; | ||
737 | u_char *nbuf; | ||
738 | struct ippp_struct *is; | ||
739 | |||
740 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
741 | printk(KERN_WARNING "ippp: illegal slot(%d).\n", slot); | ||
742 | return 0; | ||
743 | } | ||
744 | is = ippp_table[slot]; | ||
745 | |||
746 | if (!(is->state & IPPP_CONNECT)) { | ||
747 | printk(KERN_DEBUG "ippp: device not activated.\n"); | ||
748 | return 0; | ||
749 | } | ||
750 | nbuf = kmalloc(len + 4, GFP_ATOMIC); | ||
751 | if (!nbuf) { | ||
752 | printk(KERN_WARNING "ippp: Can't alloc buf\n"); | ||
753 | return 0; | ||
754 | } | ||
755 | nbuf[0] = PPP_ALLSTATIONS; | ||
756 | nbuf[1] = PPP_UI; | ||
757 | nbuf[2] = proto >> 8; | ||
758 | nbuf[3] = proto & 0xff; | ||
759 | memcpy(nbuf + 4, buf, len); | ||
760 | |||
761 | spin_lock_irqsave(&is->buflock, flags); | ||
762 | bf = is->first; | ||
763 | bl = is->last; | ||
764 | |||
765 | if (bf == bl) { | ||
766 | printk(KERN_WARNING "ippp: Queue is full; discarding first buffer\n"); | ||
767 | bf = bf->next; | ||
768 | kfree(bf->buf); | ||
769 | is->first = bf; | ||
770 | } | ||
771 | bl->buf = (char *) nbuf; | ||
772 | bl->len = len + 4; | ||
773 | |||
774 | is->last = bl->next; | ||
775 | spin_unlock_irqrestore(&is->buflock, flags); | ||
776 | wake_up_interruptible(&is->wq); | ||
777 | return len; | ||
778 | } | ||
779 | |||
780 | /* | ||
781 | * read() .. non-blocking: ipppd calls it only after select() | ||
782 | * reports, that there is data | ||
783 | */ | ||
784 | |||
785 | int | ||
786 | isdn_ppp_read(int min, struct file *file, char __user *buf, int count) | ||
787 | { | ||
788 | struct ippp_struct *is; | ||
789 | struct ippp_buf_queue *b; | ||
790 | u_long flags; | ||
791 | u_char *save_buf; | ||
792 | |||
793 | is = file->private_data; | ||
794 | |||
795 | if (!(is->state & IPPP_OPEN)) | ||
796 | return 0; | ||
797 | |||
798 | spin_lock_irqsave(&is->buflock, flags); | ||
799 | b = is->first->next; | ||
800 | save_buf = b->buf; | ||
801 | if (!save_buf) { | ||
802 | spin_unlock_irqrestore(&is->buflock, flags); | ||
803 | return -EAGAIN; | ||
804 | } | ||
805 | if (b->len < count) | ||
806 | count = b->len; | ||
807 | b->buf = NULL; | ||
808 | is->first = b; | ||
809 | |||
810 | spin_unlock_irqrestore(&is->buflock, flags); | ||
811 | if (copy_to_user(buf, save_buf, count)) | ||
812 | count = -EFAULT; | ||
813 | kfree(save_buf); | ||
814 | |||
815 | return count; | ||
816 | } | ||
817 | |||
818 | /* | ||
819 | * ipppd wanna write a packet to the card .. non-blocking | ||
820 | */ | ||
821 | |||
822 | int | ||
823 | isdn_ppp_write(int min, struct file *file, const char __user *buf, int count) | ||
824 | { | ||
825 | isdn_net_local *lp; | ||
826 | struct ippp_struct *is; | ||
827 | int proto; | ||
828 | |||
829 | is = file->private_data; | ||
830 | |||
831 | if (!(is->state & IPPP_CONNECT)) | ||
832 | return 0; | ||
833 | |||
834 | lp = is->lp; | ||
835 | |||
836 | /* -> push it directly to the lowlevel interface */ | ||
837 | |||
838 | if (!lp) | ||
839 | printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n"); | ||
840 | else { | ||
841 | if (lp->isdn_device < 0 || lp->isdn_channel < 0) { | ||
842 | unsigned char protobuf[4]; | ||
843 | /* | ||
844 | * Don't reset huptimer for | ||
845 | * LCP packets. (Echo requests). | ||
846 | */ | ||
847 | if (copy_from_user(protobuf, buf, 4)) | ||
848 | return -EFAULT; | ||
849 | |||
850 | proto = PPP_PROTOCOL(protobuf); | ||
851 | if (proto != PPP_LCP) | ||
852 | lp->huptimer = 0; | ||
853 | |||
854 | return 0; | ||
855 | } | ||
856 | |||
857 | if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) && | ||
858 | lp->dialstate == 0 && | ||
859 | (lp->flags & ISDN_NET_CONNECTED)) { | ||
860 | unsigned short hl; | ||
861 | struct sk_buff *skb; | ||
862 | unsigned char *cpy_buf; | ||
863 | /* | ||
864 | * we need to reserve enough space in front of | ||
865 | * sk_buff. old call to dev_alloc_skb only reserved | ||
866 | * 16 bytes, now we are looking what the driver want | ||
867 | */ | ||
868 | hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; | ||
869 | skb = alloc_skb(hl + count, GFP_ATOMIC); | ||
870 | if (!skb) { | ||
871 | printk(KERN_WARNING "isdn_ppp_write: out of memory!\n"); | ||
872 | return count; | ||
873 | } | ||
874 | skb_reserve(skb, hl); | ||
875 | cpy_buf = skb_put(skb, count); | ||
876 | if (copy_from_user(cpy_buf, buf, count)) | ||
877 | { | ||
878 | kfree_skb(skb); | ||
879 | return -EFAULT; | ||
880 | } | ||
881 | |||
882 | /* | ||
883 | * Don't reset huptimer for | ||
884 | * LCP packets. (Echo requests). | ||
885 | */ | ||
886 | proto = PPP_PROTOCOL(cpy_buf); | ||
887 | if (proto != PPP_LCP) | ||
888 | lp->huptimer = 0; | ||
889 | |||
890 | if (is->debug & 0x40) { | ||
891 | printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len); | ||
892 | isdn_ppp_frame_log("xmit", skb->data, skb->len, 32, is->unit, lp->ppp_slot); | ||
893 | } | ||
894 | |||
895 | isdn_ppp_send_ccp(lp->netdev, lp, skb); /* keeps CCP/compression states in sync */ | ||
896 | |||
897 | isdn_net_write_super(lp, skb); | ||
898 | } | ||
899 | } | ||
900 | return count; | ||
901 | } | ||
902 | |||
903 | /* | ||
904 | * init memory, structures etc. | ||
905 | */ | ||
906 | |||
907 | int | ||
908 | isdn_ppp_init(void) | ||
909 | { | ||
910 | int i, | ||
911 | j; | ||
912 | |||
913 | #ifdef CONFIG_ISDN_MPP | ||
914 | if (isdn_ppp_mp_bundle_array_init() < 0) | ||
915 | return -ENOMEM; | ||
916 | #endif /* CONFIG_ISDN_MPP */ | ||
917 | |||
918 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
919 | if (!(ippp_table[i] = kzalloc(sizeof(struct ippp_struct), GFP_KERNEL))) { | ||
920 | printk(KERN_WARNING "isdn_ppp_init: Could not alloc ippp_table\n"); | ||
921 | for (j = 0; j < i; j++) | ||
922 | kfree(ippp_table[j]); | ||
923 | return -1; | ||
924 | } | ||
925 | spin_lock_init(&ippp_table[i]->buflock); | ||
926 | ippp_table[i]->state = 0; | ||
927 | ippp_table[i]->first = ippp_table[i]->rq + NUM_RCV_BUFFS - 1; | ||
928 | ippp_table[i]->last = ippp_table[i]->rq; | ||
929 | |||
930 | for (j = 0; j < NUM_RCV_BUFFS; j++) { | ||
931 | ippp_table[i]->rq[j].buf = NULL; | ||
932 | ippp_table[i]->rq[j].last = ippp_table[i]->rq + | ||
933 | (NUM_RCV_BUFFS + j - 1) % NUM_RCV_BUFFS; | ||
934 | ippp_table[i]->rq[j].next = ippp_table[i]->rq + (j + 1) % NUM_RCV_BUFFS; | ||
935 | } | ||
936 | } | ||
937 | return 0; | ||
938 | } | ||
939 | |||
940 | void | ||
941 | isdn_ppp_cleanup(void) | ||
942 | { | ||
943 | int i; | ||
944 | |||
945 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
946 | kfree(ippp_table[i]); | ||
947 | |||
948 | #ifdef CONFIG_ISDN_MPP | ||
949 | kfree(isdn_ppp_bundle_arr); | ||
950 | #endif /* CONFIG_ISDN_MPP */ | ||
951 | |||
952 | } | ||
953 | |||
954 | /* | ||
955 | * check for address/control field and skip if allowed | ||
956 | * retval != 0 -> discard packet silently | ||
957 | */ | ||
958 | static int isdn_ppp_skip_ac(struct ippp_struct *is, struct sk_buff *skb) | ||
959 | { | ||
960 | if (skb->len < 1) | ||
961 | return -1; | ||
962 | |||
963 | if (skb->data[0] == 0xff) { | ||
964 | if (skb->len < 2) | ||
965 | return -1; | ||
966 | |||
967 | if (skb->data[1] != 0x03) | ||
968 | return -1; | ||
969 | |||
970 | // skip address/control (AC) field | ||
971 | skb_pull(skb, 2); | ||
972 | } else { | ||
973 | if (is->pppcfg & SC_REJ_COMP_AC) | ||
974 | // if AC compression was not negotiated, but used, discard packet | ||
975 | return -1; | ||
976 | } | ||
977 | return 0; | ||
978 | } | ||
979 | |||
980 | /* | ||
981 | * get the PPP protocol header and pull skb | ||
982 | * retval < 0 -> discard packet silently | ||
983 | */ | ||
984 | static int isdn_ppp_strip_proto(struct sk_buff *skb) | ||
985 | { | ||
986 | int proto; | ||
987 | |||
988 | if (skb->len < 1) | ||
989 | return -1; | ||
990 | |||
991 | if (skb->data[0] & 0x1) { | ||
992 | // protocol field is compressed | ||
993 | proto = skb->data[0]; | ||
994 | skb_pull(skb, 1); | ||
995 | } else { | ||
996 | if (skb->len < 2) | ||
997 | return -1; | ||
998 | proto = ((int) skb->data[0] << 8) + skb->data[1]; | ||
999 | skb_pull(skb, 2); | ||
1000 | } | ||
1001 | return proto; | ||
1002 | } | ||
1003 | |||
1004 | |||
1005 | /* | ||
1006 | * handler for incoming packets on a syncPPP interface | ||
1007 | */ | ||
1008 | void isdn_ppp_receive(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb) | ||
1009 | { | ||
1010 | struct ippp_struct *is; | ||
1011 | int slot; | ||
1012 | int proto; | ||
1013 | |||
1014 | BUG_ON(net_dev->local->master); // we're called with the master device always | ||
1015 | |||
1016 | slot = lp->ppp_slot; | ||
1017 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
1018 | printk(KERN_ERR "isdn_ppp_receive: lp->ppp_slot(%d)\n", | ||
1019 | lp->ppp_slot); | ||
1020 | kfree_skb(skb); | ||
1021 | return; | ||
1022 | } | ||
1023 | is = ippp_table[slot]; | ||
1024 | |||
1025 | if (is->debug & 0x4) { | ||
1026 | printk(KERN_DEBUG "ippp_receive: is:%08lx lp:%08lx slot:%d unit:%d len:%d\n", | ||
1027 | (long)is, (long)lp, lp->ppp_slot, is->unit, (int)skb->len); | ||
1028 | isdn_ppp_frame_log("receive", skb->data, skb->len, 32, is->unit, lp->ppp_slot); | ||
1029 | } | ||
1030 | |||
1031 | if (isdn_ppp_skip_ac(is, skb) < 0) { | ||
1032 | kfree_skb(skb); | ||
1033 | return; | ||
1034 | } | ||
1035 | proto = isdn_ppp_strip_proto(skb); | ||
1036 | if (proto < 0) { | ||
1037 | kfree_skb(skb); | ||
1038 | return; | ||
1039 | } | ||
1040 | |||
1041 | #ifdef CONFIG_ISDN_MPP | ||
1042 | if (is->compflags & SC_LINK_DECOMP_ON) { | ||
1043 | skb = isdn_ppp_decompress(skb, is, NULL, &proto); | ||
1044 | if (!skb) // decompression error | ||
1045 | return; | ||
1046 | } | ||
1047 | |||
1048 | if (!(is->mpppcfg & SC_REJ_MP_PROT)) { // we agreed to receive MPPP | ||
1049 | if (proto == PPP_MP) { | ||
1050 | isdn_ppp_mp_receive(net_dev, lp, skb); | ||
1051 | return; | ||
1052 | } | ||
1053 | } | ||
1054 | #endif | ||
1055 | isdn_ppp_push_higher(net_dev, lp, skb, proto); | ||
1056 | } | ||
1057 | |||
1058 | /* | ||
1059 | * we receive a reassembled frame, MPPP has been taken care of before. | ||
1060 | * address/control and protocol have been stripped from the skb | ||
1061 | * note: net_dev has to be master net_dev | ||
1062 | */ | ||
1063 | static void | ||
1064 | isdn_ppp_push_higher(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb, int proto) | ||
1065 | { | ||
1066 | struct net_device *dev = net_dev->dev; | ||
1067 | struct ippp_struct *is, *mis; | ||
1068 | isdn_net_local *mlp = NULL; | ||
1069 | int slot; | ||
1070 | |||
1071 | slot = lp->ppp_slot; | ||
1072 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
1073 | printk(KERN_ERR "isdn_ppp_push_higher: lp->ppp_slot(%d)\n", | ||
1074 | lp->ppp_slot); | ||
1075 | goto drop_packet; | ||
1076 | } | ||
1077 | is = ippp_table[slot]; | ||
1078 | |||
1079 | if (lp->master) { // FIXME? | ||
1080 | mlp = ISDN_MASTER_PRIV(lp); | ||
1081 | slot = mlp->ppp_slot; | ||
1082 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
1083 | printk(KERN_ERR "isdn_ppp_push_higher: master->ppp_slot(%d)\n", | ||
1084 | lp->ppp_slot); | ||
1085 | goto drop_packet; | ||
1086 | } | ||
1087 | } | ||
1088 | mis = ippp_table[slot]; | ||
1089 | |||
1090 | if (is->debug & 0x10) { | ||
1091 | printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto); | ||
1092 | isdn_ppp_frame_log("rpush", skb->data, skb->len, 32, is->unit, lp->ppp_slot); | ||
1093 | } | ||
1094 | if (mis->compflags & SC_DECOMP_ON) { | ||
1095 | skb = isdn_ppp_decompress(skb, is, mis, &proto); | ||
1096 | if (!skb) // decompression error | ||
1097 | return; | ||
1098 | } | ||
1099 | switch (proto) { | ||
1100 | case PPP_IPX: /* untested */ | ||
1101 | if (is->debug & 0x20) | ||
1102 | printk(KERN_DEBUG "isdn_ppp: IPX\n"); | ||
1103 | skb->protocol = htons(ETH_P_IPX); | ||
1104 | break; | ||
1105 | case PPP_IP: | ||
1106 | if (is->debug & 0x20) | ||
1107 | printk(KERN_DEBUG "isdn_ppp: IP\n"); | ||
1108 | skb->protocol = htons(ETH_P_IP); | ||
1109 | break; | ||
1110 | case PPP_COMP: | ||
1111 | case PPP_COMPFRAG: | ||
1112 | printk(KERN_INFO "isdn_ppp: unexpected compressed frame dropped\n"); | ||
1113 | goto drop_packet; | ||
1114 | #ifdef CONFIG_ISDN_PPP_VJ | ||
1115 | case PPP_VJC_UNCOMP: | ||
1116 | if (is->debug & 0x20) | ||
1117 | printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n"); | ||
1118 | if (net_dev->local->ppp_slot < 0) { | ||
1119 | printk(KERN_ERR "%s: net_dev->local->ppp_slot(%d) out of range\n", | ||
1120 | __func__, net_dev->local->ppp_slot); | ||
1121 | goto drop_packet; | ||
1122 | } | ||
1123 | if (slhc_remember(ippp_table[net_dev->local->ppp_slot]->slcomp, skb->data, skb->len) <= 0) { | ||
1124 | printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n"); | ||
1125 | goto drop_packet; | ||
1126 | } | ||
1127 | skb->protocol = htons(ETH_P_IP); | ||
1128 | break; | ||
1129 | case PPP_VJC_COMP: | ||
1130 | if (is->debug & 0x20) | ||
1131 | printk(KERN_DEBUG "isdn_ppp: VJC_COMP\n"); | ||
1132 | { | ||
1133 | struct sk_buff *skb_old = skb; | ||
1134 | int pkt_len; | ||
1135 | skb = dev_alloc_skb(skb_old->len + 128); | ||
1136 | |||
1137 | if (!skb) { | ||
1138 | printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name); | ||
1139 | skb = skb_old; | ||
1140 | goto drop_packet; | ||
1141 | } | ||
1142 | skb_put(skb, skb_old->len + 128); | ||
1143 | skb_copy_from_linear_data(skb_old, skb->data, | ||
1144 | skb_old->len); | ||
1145 | if (net_dev->local->ppp_slot < 0) { | ||
1146 | printk(KERN_ERR "%s: net_dev->local->ppp_slot(%d) out of range\n", | ||
1147 | __func__, net_dev->local->ppp_slot); | ||
1148 | goto drop_packet; | ||
1149 | } | ||
1150 | pkt_len = slhc_uncompress(ippp_table[net_dev->local->ppp_slot]->slcomp, | ||
1151 | skb->data, skb_old->len); | ||
1152 | kfree_skb(skb_old); | ||
1153 | if (pkt_len < 0) | ||
1154 | goto drop_packet; | ||
1155 | |||
1156 | skb_trim(skb, pkt_len); | ||
1157 | skb->protocol = htons(ETH_P_IP); | ||
1158 | } | ||
1159 | break; | ||
1160 | #endif | ||
1161 | case PPP_CCP: | ||
1162 | case PPP_CCPFRAG: | ||
1163 | isdn_ppp_receive_ccp(net_dev, lp, skb, proto); | ||
1164 | /* Dont pop up ResetReq/Ack stuff to the daemon any | ||
1165 | longer - the job is done already */ | ||
1166 | if (skb->data[0] == CCP_RESETREQ || | ||
1167 | skb->data[0] == CCP_RESETACK) | ||
1168 | break; | ||
1169 | /* fall through */ | ||
1170 | default: | ||
1171 | isdn_ppp_fill_rq(skb->data, skb->len, proto, lp->ppp_slot); /* push data to pppd device */ | ||
1172 | kfree_skb(skb); | ||
1173 | return; | ||
1174 | } | ||
1175 | |||
1176 | #ifdef CONFIG_IPPP_FILTER | ||
1177 | /* check if the packet passes the pass and active filters | ||
1178 | * the filter instructions are constructed assuming | ||
1179 | * a four-byte PPP header on each packet (which is still present) */ | ||
1180 | skb_push(skb, 4); | ||
1181 | |||
1182 | { | ||
1183 | u_int16_t *p = (u_int16_t *) skb->data; | ||
1184 | |||
1185 | *p = 0; /* indicate inbound */ | ||
1186 | } | ||
1187 | |||
1188 | if (is->pass_filter | ||
1189 | && BPF_PROG_RUN(is->pass_filter, skb) == 0) { | ||
1190 | if (is->debug & 0x2) | ||
1191 | printk(KERN_DEBUG "IPPP: inbound frame filtered.\n"); | ||
1192 | kfree_skb(skb); | ||
1193 | return; | ||
1194 | } | ||
1195 | if (!(is->active_filter | ||
1196 | && BPF_PROG_RUN(is->active_filter, skb) == 0)) { | ||
1197 | if (is->debug & 0x2) | ||
1198 | printk(KERN_DEBUG "IPPP: link-active filter: resetting huptimer.\n"); | ||
1199 | lp->huptimer = 0; | ||
1200 | if (mlp) | ||
1201 | mlp->huptimer = 0; | ||
1202 | } | ||
1203 | skb_pull(skb, 4); | ||
1204 | #else /* CONFIG_IPPP_FILTER */ | ||
1205 | lp->huptimer = 0; | ||
1206 | if (mlp) | ||
1207 | mlp->huptimer = 0; | ||
1208 | #endif /* CONFIG_IPPP_FILTER */ | ||
1209 | skb->dev = dev; | ||
1210 | skb_reset_mac_header(skb); | ||
1211 | netif_rx(skb); | ||
1212 | /* net_dev->local->stats.rx_packets++; done in isdn_net.c */ | ||
1213 | return; | ||
1214 | |||
1215 | drop_packet: | ||
1216 | net_dev->local->stats.rx_dropped++; | ||
1217 | kfree_skb(skb); | ||
1218 | } | ||
1219 | |||
1220 | /* | ||
1221 | * isdn_ppp_skb_push .. | ||
1222 | * checks whether we have enough space at the beginning of the skb | ||
1223 | * and allocs a new SKB if necessary | ||
1224 | */ | ||
1225 | static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p, int len) | ||
1226 | { | ||
1227 | struct sk_buff *skb = *skb_p; | ||
1228 | |||
1229 | if (skb_headroom(skb) < len) { | ||
1230 | struct sk_buff *nskb = skb_realloc_headroom(skb, len); | ||
1231 | |||
1232 | if (!nskb) { | ||
1233 | printk(KERN_ERR "isdn_ppp_skb_push: can't realloc headroom!\n"); | ||
1234 | dev_kfree_skb(skb); | ||
1235 | return NULL; | ||
1236 | } | ||
1237 | printk(KERN_DEBUG "isdn_ppp_skb_push:under %d %d\n", skb_headroom(skb), len); | ||
1238 | dev_kfree_skb(skb); | ||
1239 | *skb_p = nskb; | ||
1240 | return skb_push(nskb, len); | ||
1241 | } | ||
1242 | return skb_push(skb, len); | ||
1243 | } | ||
1244 | |||
1245 | /* | ||
1246 | * send ppp frame .. we expect a PIDCOMPressable proto -- | ||
1247 | * (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP) | ||
1248 | * | ||
1249 | * VJ compression may change skb pointer!!! .. requeue with old | ||
1250 | * skb isn't allowed!! | ||
1251 | */ | ||
1252 | |||
1253 | int | ||
1254 | isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev) | ||
1255 | { | ||
1256 | isdn_net_local *lp, *mlp; | ||
1257 | isdn_net_dev *nd; | ||
1258 | unsigned int proto = PPP_IP; /* 0x21 */ | ||
1259 | struct ippp_struct *ipt, *ipts; | ||
1260 | int slot, retval = NETDEV_TX_OK; | ||
1261 | |||
1262 | mlp = netdev_priv(netdev); | ||
1263 | nd = mlp->netdev; /* get master lp */ | ||
1264 | |||
1265 | slot = mlp->ppp_slot; | ||
1266 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
1267 | printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot(%d)\n", | ||
1268 | mlp->ppp_slot); | ||
1269 | kfree_skb(skb); | ||
1270 | goto out; | ||
1271 | } | ||
1272 | ipts = ippp_table[slot]; | ||
1273 | |||
1274 | if (!(ipts->pppcfg & SC_ENABLE_IP)) { /* PPP connected ? */ | ||
1275 | if (ipts->debug & 0x1) | ||
1276 | printk(KERN_INFO "%s: IP frame delayed.\n", netdev->name); | ||
1277 | retval = NETDEV_TX_BUSY; | ||
1278 | goto out; | ||
1279 | } | ||
1280 | |||
1281 | switch (ntohs(skb->protocol)) { | ||
1282 | case ETH_P_IP: | ||
1283 | proto = PPP_IP; | ||
1284 | break; | ||
1285 | case ETH_P_IPX: | ||
1286 | proto = PPP_IPX; /* untested */ | ||
1287 | break; | ||
1288 | default: | ||
1289 | printk(KERN_ERR "isdn_ppp: skipped unsupported protocol: %#x.\n", | ||
1290 | skb->protocol); | ||
1291 | dev_kfree_skb(skb); | ||
1292 | goto out; | ||
1293 | } | ||
1294 | |||
1295 | lp = isdn_net_get_locked_lp(nd); | ||
1296 | if (!lp) { | ||
1297 | printk(KERN_WARNING "%s: all channels busy - requeuing!\n", netdev->name); | ||
1298 | retval = NETDEV_TX_BUSY; | ||
1299 | goto out; | ||
1300 | } | ||
1301 | /* we have our lp locked from now on */ | ||
1302 | |||
1303 | slot = lp->ppp_slot; | ||
1304 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
1305 | printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot(%d)\n", | ||
1306 | lp->ppp_slot); | ||
1307 | kfree_skb(skb); | ||
1308 | goto unlock; | ||
1309 | } | ||
1310 | ipt = ippp_table[slot]; | ||
1311 | |||
1312 | /* | ||
1313 | * after this line .. requeueing in the device queue is no longer allowed!!! | ||
1314 | */ | ||
1315 | |||
1316 | /* Pull off the fake header we stuck on earlier to keep | ||
1317 | * the fragmentation code happy. | ||
1318 | */ | ||
1319 | skb_pull(skb, IPPP_MAX_HEADER); | ||
1320 | |||
1321 | #ifdef CONFIG_IPPP_FILTER | ||
1322 | /* check if we should pass this packet | ||
1323 | * the filter instructions are constructed assuming | ||
1324 | * a four-byte PPP header on each packet */ | ||
1325 | *(u8 *)skb_push(skb, 4) = 1; /* indicate outbound */ | ||
1326 | |||
1327 | { | ||
1328 | __be16 *p = (__be16 *)skb->data; | ||
1329 | |||
1330 | p++; | ||
1331 | *p = htons(proto); | ||
1332 | } | ||
1333 | |||
1334 | if (ipt->pass_filter | ||
1335 | && BPF_PROG_RUN(ipt->pass_filter, skb) == 0) { | ||
1336 | if (ipt->debug & 0x4) | ||
1337 | printk(KERN_DEBUG "IPPP: outbound frame filtered.\n"); | ||
1338 | kfree_skb(skb); | ||
1339 | goto unlock; | ||
1340 | } | ||
1341 | if (!(ipt->active_filter | ||
1342 | && BPF_PROG_RUN(ipt->active_filter, skb) == 0)) { | ||
1343 | if (ipt->debug & 0x4) | ||
1344 | printk(KERN_DEBUG "IPPP: link-active filter: resetting huptimer.\n"); | ||
1345 | lp->huptimer = 0; | ||
1346 | } | ||
1347 | skb_pull(skb, 4); | ||
1348 | #else /* CONFIG_IPPP_FILTER */ | ||
1349 | lp->huptimer = 0; | ||
1350 | #endif /* CONFIG_IPPP_FILTER */ | ||
1351 | |||
1352 | if (ipt->debug & 0x4) | ||
1353 | printk(KERN_DEBUG "xmit skb, len %d\n", (int) skb->len); | ||
1354 | if (ipts->debug & 0x40) | ||
1355 | isdn_ppp_frame_log("xmit0", skb->data, skb->len, 32, ipts->unit, lp->ppp_slot); | ||
1356 | |||
1357 | #ifdef CONFIG_ISDN_PPP_VJ | ||
1358 | if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) { /* ipts here? probably yes, but check this again */ | ||
1359 | struct sk_buff *new_skb; | ||
1360 | unsigned short hl; | ||
1361 | /* | ||
1362 | * we need to reserve enough space in front of | ||
1363 | * sk_buff. old call to dev_alloc_skb only reserved | ||
1364 | * 16 bytes, now we are looking what the driver want. | ||
1365 | */ | ||
1366 | hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen + IPPP_MAX_HEADER; | ||
1367 | /* | ||
1368 | * Note: hl might still be insufficient because the method | ||
1369 | * above does not account for a possibible MPPP slave channel | ||
1370 | * which had larger HL header space requirements than the | ||
1371 | * master. | ||
1372 | */ | ||
1373 | new_skb = alloc_skb(hl + skb->len, GFP_ATOMIC); | ||
1374 | if (new_skb) { | ||
1375 | u_char *buf; | ||
1376 | int pktlen; | ||
1377 | |||
1378 | skb_reserve(new_skb, hl); | ||
1379 | new_skb->dev = skb->dev; | ||
1380 | skb_put(new_skb, skb->len); | ||
1381 | buf = skb->data; | ||
1382 | |||
1383 | pktlen = slhc_compress(ipts->slcomp, skb->data, skb->len, new_skb->data, | ||
1384 | &buf, !(ipts->pppcfg & SC_NO_TCP_CCID)); | ||
1385 | |||
1386 | if (buf != skb->data) { | ||
1387 | if (new_skb->data != buf) | ||
1388 | printk(KERN_ERR "isdn_ppp: FATAL error after slhc_compress!!\n"); | ||
1389 | dev_kfree_skb(skb); | ||
1390 | skb = new_skb; | ||
1391 | } else { | ||
1392 | dev_kfree_skb(new_skb); | ||
1393 | } | ||
1394 | |||
1395 | skb_trim(skb, pktlen); | ||
1396 | if (skb->data[0] & SL_TYPE_COMPRESSED_TCP) { /* cslip? style -> PPP */ | ||
1397 | proto = PPP_VJC_COMP; | ||
1398 | skb->data[0] ^= SL_TYPE_COMPRESSED_TCP; | ||
1399 | } else { | ||
1400 | if (skb->data[0] >= SL_TYPE_UNCOMPRESSED_TCP) | ||
1401 | proto = PPP_VJC_UNCOMP; | ||
1402 | skb->data[0] = (skb->data[0] & 0x0f) | 0x40; | ||
1403 | } | ||
1404 | } | ||
1405 | } | ||
1406 | #endif | ||
1407 | |||
1408 | /* | ||
1409 | * normal (single link) or bundle compression | ||
1410 | */ | ||
1411 | if (ipts->compflags & SC_COMP_ON) { | ||
1412 | /* We send compressed only if both down- und upstream | ||
1413 | compression is negotiated, that means, CCP is up */ | ||
1414 | if (ipts->compflags & SC_DECOMP_ON) { | ||
1415 | skb = isdn_ppp_compress(skb, &proto, ipt, ipts, 0); | ||
1416 | } else { | ||
1417 | printk(KERN_DEBUG "isdn_ppp: CCP not yet up - sending as-is\n"); | ||
1418 | } | ||
1419 | } | ||
1420 | |||
1421 | if (ipt->debug & 0x24) | ||
1422 | printk(KERN_DEBUG "xmit2 skb, len %d, proto %04x\n", (int) skb->len, proto); | ||
1423 | |||
1424 | #ifdef CONFIG_ISDN_MPP | ||
1425 | if (ipt->mpppcfg & SC_MP_PROT) { | ||
1426 | /* we get mp_seqno from static isdn_net_local */ | ||
1427 | long mp_seqno = ipts->mp_seqno; | ||
1428 | ipts->mp_seqno++; | ||
1429 | if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) { | ||
1430 | unsigned char *data = isdn_ppp_skb_push(&skb, 3); | ||
1431 | if (!data) | ||
1432 | goto unlock; | ||
1433 | mp_seqno &= 0xfff; | ||
1434 | data[0] = MP_BEGIN_FRAG | MP_END_FRAG | ((mp_seqno >> 8) & 0xf); /* (B)egin & (E)ndbit .. */ | ||
1435 | data[1] = mp_seqno & 0xff; | ||
1436 | data[2] = proto; /* PID compression */ | ||
1437 | } else { | ||
1438 | unsigned char *data = isdn_ppp_skb_push(&skb, 5); | ||
1439 | if (!data) | ||
1440 | goto unlock; | ||
1441 | data[0] = MP_BEGIN_FRAG | MP_END_FRAG; /* (B)egin & (E)ndbit .. */ | ||
1442 | data[1] = (mp_seqno >> 16) & 0xff; /* sequence number: 24bit */ | ||
1443 | data[2] = (mp_seqno >> 8) & 0xff; | ||
1444 | data[3] = (mp_seqno >> 0) & 0xff; | ||
1445 | data[4] = proto; /* PID compression */ | ||
1446 | } | ||
1447 | proto = PPP_MP; /* MP Protocol, 0x003d */ | ||
1448 | } | ||
1449 | #endif | ||
1450 | |||
1451 | /* | ||
1452 | * 'link in bundle' compression ... | ||
1453 | */ | ||
1454 | if (ipt->compflags & SC_LINK_COMP_ON) | ||
1455 | skb = isdn_ppp_compress(skb, &proto, ipt, ipts, 1); | ||
1456 | |||
1457 | if ((ipt->pppcfg & SC_COMP_PROT) && (proto <= 0xff)) { | ||
1458 | unsigned char *data = isdn_ppp_skb_push(&skb, 1); | ||
1459 | if (!data) | ||
1460 | goto unlock; | ||
1461 | data[0] = proto & 0xff; | ||
1462 | } | ||
1463 | else { | ||
1464 | unsigned char *data = isdn_ppp_skb_push(&skb, 2); | ||
1465 | if (!data) | ||
1466 | goto unlock; | ||
1467 | data[0] = (proto >> 8) & 0xff; | ||
1468 | data[1] = proto & 0xff; | ||
1469 | } | ||
1470 | if (!(ipt->pppcfg & SC_COMP_AC)) { | ||
1471 | unsigned char *data = isdn_ppp_skb_push(&skb, 2); | ||
1472 | if (!data) | ||
1473 | goto unlock; | ||
1474 | data[0] = 0xff; /* All Stations */ | ||
1475 | data[1] = 0x03; /* Unnumbered information */ | ||
1476 | } | ||
1477 | |||
1478 | /* tx-stats are now updated via BSENT-callback */ | ||
1479 | |||
1480 | if (ipts->debug & 0x40) { | ||
1481 | printk(KERN_DEBUG "skb xmit: len: %d\n", (int) skb->len); | ||
1482 | isdn_ppp_frame_log("xmit", skb->data, skb->len, 32, ipt->unit, lp->ppp_slot); | ||
1483 | } | ||
1484 | |||
1485 | isdn_net_writebuf_skb(lp, skb); | ||
1486 | |||
1487 | unlock: | ||
1488 | spin_unlock_bh(&lp->xmit_lock); | ||
1489 | out: | ||
1490 | return retval; | ||
1491 | } | ||
1492 | |||
1493 | #ifdef CONFIG_IPPP_FILTER | ||
1494 | /* | ||
1495 | * check if this packet may trigger auto-dial. | ||
1496 | */ | ||
1497 | |||
1498 | int isdn_ppp_autodial_filter(struct sk_buff *skb, isdn_net_local *lp) | ||
1499 | { | ||
1500 | struct ippp_struct *is = ippp_table[lp->ppp_slot]; | ||
1501 | u_int16_t proto; | ||
1502 | int drop = 0; | ||
1503 | |||
1504 | switch (ntohs(skb->protocol)) { | ||
1505 | case ETH_P_IP: | ||
1506 | proto = PPP_IP; | ||
1507 | break; | ||
1508 | case ETH_P_IPX: | ||
1509 | proto = PPP_IPX; | ||
1510 | break; | ||
1511 | default: | ||
1512 | printk(KERN_ERR "isdn_ppp_autodial_filter: unsupported protocol 0x%x.\n", | ||
1513 | skb->protocol); | ||
1514 | return 1; | ||
1515 | } | ||
1516 | |||
1517 | /* the filter instructions are constructed assuming | ||
1518 | * a four-byte PPP header on each packet. we have to | ||
1519 | * temporarily remove part of the fake header stuck on | ||
1520 | * earlier. | ||
1521 | */ | ||
1522 | *(u8 *)skb_pull(skb, IPPP_MAX_HEADER - 4) = 1; /* indicate outbound */ | ||
1523 | |||
1524 | { | ||
1525 | __be16 *p = (__be16 *)skb->data; | ||
1526 | |||
1527 | p++; | ||
1528 | *p = htons(proto); | ||
1529 | } | ||
1530 | |||
1531 | drop |= is->pass_filter | ||
1532 | && BPF_PROG_RUN(is->pass_filter, skb) == 0; | ||
1533 | drop |= is->active_filter | ||
1534 | && BPF_PROG_RUN(is->active_filter, skb) == 0; | ||
1535 | |||
1536 | skb_push(skb, IPPP_MAX_HEADER - 4); | ||
1537 | return drop; | ||
1538 | } | ||
1539 | #endif | ||
1540 | #ifdef CONFIG_ISDN_MPP | ||
1541 | |||
1542 | /* this is _not_ rfc1990 header, but something we convert both short and long | ||
1543 | * headers to for convinience's sake: | ||
1544 | * byte 0 is flags as in rfc1990 | ||
1545 | * bytes 1...4 is 24-bit seqence number converted to host byte order | ||
1546 | */ | ||
1547 | #define MP_HEADER_LEN 5 | ||
1548 | |||
1549 | #define MP_LONGSEQ_MASK 0x00ffffff | ||
1550 | #define MP_SHORTSEQ_MASK 0x00000fff | ||
1551 | #define MP_LONGSEQ_MAX MP_LONGSEQ_MASK | ||
1552 | #define MP_SHORTSEQ_MAX MP_SHORTSEQ_MASK | ||
1553 | #define MP_LONGSEQ_MAXBIT ((MP_LONGSEQ_MASK + 1) >> 1) | ||
1554 | #define MP_SHORTSEQ_MAXBIT ((MP_SHORTSEQ_MASK + 1) >> 1) | ||
1555 | |||
1556 | /* sequence-wrap safe comparisons (for long sequence)*/ | ||
1557 | #define MP_LT(a, b) ((a - b) & MP_LONGSEQ_MAXBIT) | ||
1558 | #define MP_LE(a, b) !((b - a) & MP_LONGSEQ_MAXBIT) | ||
1559 | #define MP_GT(a, b) ((b - a) & MP_LONGSEQ_MAXBIT) | ||
1560 | #define MP_GE(a, b) !((a - b) & MP_LONGSEQ_MAXBIT) | ||
1561 | |||
1562 | #define MP_SEQ(f) ((*(u32 *)(f->data + 1))) | ||
1563 | #define MP_FLAGS(f) (f->data[0]) | ||
1564 | |||
1565 | static int isdn_ppp_mp_bundle_array_init(void) | ||
1566 | { | ||
1567 | int i; | ||
1568 | int sz = ISDN_MAX_CHANNELS * sizeof(ippp_bundle); | ||
1569 | if ((isdn_ppp_bundle_arr = kzalloc(sz, GFP_KERNEL)) == NULL) | ||
1570 | return -ENOMEM; | ||
1571 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
1572 | spin_lock_init(&isdn_ppp_bundle_arr[i].lock); | ||
1573 | return 0; | ||
1574 | } | ||
1575 | |||
1576 | static ippp_bundle *isdn_ppp_mp_bundle_alloc(void) | ||
1577 | { | ||
1578 | int i; | ||
1579 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
1580 | if (isdn_ppp_bundle_arr[i].ref_ct <= 0) | ||
1581 | return (isdn_ppp_bundle_arr + i); | ||
1582 | return NULL; | ||
1583 | } | ||
1584 | |||
1585 | static int isdn_ppp_mp_init(isdn_net_local *lp, ippp_bundle *add_to) | ||
1586 | { | ||
1587 | struct ippp_struct *is; | ||
1588 | |||
1589 | if (lp->ppp_slot < 0) { | ||
1590 | printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n", | ||
1591 | __func__, lp->ppp_slot); | ||
1592 | return (-EINVAL); | ||
1593 | } | ||
1594 | |||
1595 | is = ippp_table[lp->ppp_slot]; | ||
1596 | if (add_to) { | ||
1597 | if (lp->netdev->pb) | ||
1598 | lp->netdev->pb->ref_ct--; | ||
1599 | lp->netdev->pb = add_to; | ||
1600 | } else { /* first link in a bundle */ | ||
1601 | is->mp_seqno = 0; | ||
1602 | if ((lp->netdev->pb = isdn_ppp_mp_bundle_alloc()) == NULL) | ||
1603 | return -ENOMEM; | ||
1604 | lp->next = lp->last = lp; /* nobody else in a queue */ | ||
1605 | lp->netdev->pb->frags = NULL; | ||
1606 | lp->netdev->pb->frames = 0; | ||
1607 | lp->netdev->pb->seq = UINT_MAX; | ||
1608 | } | ||
1609 | lp->netdev->pb->ref_ct++; | ||
1610 | |||
1611 | is->last_link_seqno = 0; | ||
1612 | return 0; | ||
1613 | } | ||
1614 | |||
1615 | static u32 isdn_ppp_mp_get_seq(int short_seq, | ||
1616 | struct sk_buff *skb, u32 last_seq); | ||
1617 | static struct sk_buff *isdn_ppp_mp_discard(ippp_bundle *mp, | ||
1618 | struct sk_buff *from, struct sk_buff *to); | ||
1619 | static void isdn_ppp_mp_reassembly(isdn_net_dev *net_dev, isdn_net_local *lp, | ||
1620 | struct sk_buff *from, struct sk_buff *to); | ||
1621 | static void isdn_ppp_mp_free_skb(ippp_bundle *mp, struct sk_buff *skb); | ||
1622 | static void isdn_ppp_mp_print_recv_pkt(int slot, struct sk_buff *skb); | ||
1623 | |||
1624 | static void isdn_ppp_mp_receive(isdn_net_dev *net_dev, isdn_net_local *lp, | ||
1625 | struct sk_buff *skb) | ||
1626 | { | ||
1627 | struct ippp_struct *is; | ||
1628 | isdn_net_local *lpq; | ||
1629 | ippp_bundle *mp; | ||
1630 | isdn_mppp_stats *stats; | ||
1631 | struct sk_buff *newfrag, *frag, *start, *nextf; | ||
1632 | u32 newseq, minseq, thisseq; | ||
1633 | unsigned long flags; | ||
1634 | int slot; | ||
1635 | |||
1636 | spin_lock_irqsave(&net_dev->pb->lock, flags); | ||
1637 | mp = net_dev->pb; | ||
1638 | stats = &mp->stats; | ||
1639 | slot = lp->ppp_slot; | ||
1640 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
1641 | printk(KERN_ERR "%s: lp->ppp_slot(%d)\n", | ||
1642 | __func__, lp->ppp_slot); | ||
1643 | stats->frame_drops++; | ||
1644 | dev_kfree_skb(skb); | ||
1645 | spin_unlock_irqrestore(&mp->lock, flags); | ||
1646 | return; | ||
1647 | } | ||
1648 | is = ippp_table[slot]; | ||
1649 | if (++mp->frames > stats->max_queue_len) | ||
1650 | stats->max_queue_len = mp->frames; | ||
1651 | |||
1652 | if (is->debug & 0x8) | ||
1653 | isdn_ppp_mp_print_recv_pkt(lp->ppp_slot, skb); | ||
1654 | |||
1655 | newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ, | ||
1656 | skb, is->last_link_seqno); | ||
1657 | |||
1658 | |||
1659 | /* if this packet seq # is less than last already processed one, | ||
1660 | * toss it right away, but check for sequence start case first | ||
1661 | */ | ||
1662 | if (mp->seq > MP_LONGSEQ_MAX && (newseq & MP_LONGSEQ_MAXBIT)) { | ||
1663 | mp->seq = newseq; /* the first packet: required for | ||
1664 | * rfc1990 non-compliant clients -- | ||
1665 | * prevents constant packet toss */ | ||
1666 | } else if (MP_LT(newseq, mp->seq)) { | ||
1667 | stats->frame_drops++; | ||
1668 | isdn_ppp_mp_free_skb(mp, skb); | ||
1669 | spin_unlock_irqrestore(&mp->lock, flags); | ||
1670 | return; | ||
1671 | } | ||
1672 | |||
1673 | /* find the minimum received sequence number over all links */ | ||
1674 | is->last_link_seqno = minseq = newseq; | ||
1675 | for (lpq = net_dev->queue;;) { | ||
1676 | slot = lpq->ppp_slot; | ||
1677 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
1678 | printk(KERN_ERR "%s: lpq->ppp_slot(%d)\n", | ||
1679 | __func__, lpq->ppp_slot); | ||
1680 | } else { | ||
1681 | u32 lls = ippp_table[slot]->last_link_seqno; | ||
1682 | if (MP_LT(lls, minseq)) | ||
1683 | minseq = lls; | ||
1684 | } | ||
1685 | if ((lpq = lpq->next) == net_dev->queue) | ||
1686 | break; | ||
1687 | } | ||
1688 | if (MP_LT(minseq, mp->seq)) | ||
1689 | minseq = mp->seq; /* can't go beyond already processed | ||
1690 | * packets */ | ||
1691 | newfrag = skb; | ||
1692 | |||
1693 | /* if this new fragment is before the first one, then enqueue it now. */ | ||
1694 | if ((frag = mp->frags) == NULL || MP_LT(newseq, MP_SEQ(frag))) { | ||
1695 | newfrag->next = frag; | ||
1696 | mp->frags = frag = newfrag; | ||
1697 | newfrag = NULL; | ||
1698 | } | ||
1699 | |||
1700 | start = MP_FLAGS(frag) & MP_BEGIN_FRAG && | ||
1701 | MP_SEQ(frag) == mp->seq ? frag : NULL; | ||
1702 | |||
1703 | /* | ||
1704 | * main fragment traversing loop | ||
1705 | * | ||
1706 | * try to accomplish several tasks: | ||
1707 | * - insert new fragment into the proper sequence slot (once that's done | ||
1708 | * newfrag will be set to NULL) | ||
1709 | * - reassemble any complete fragment sequence (non-null 'start' | ||
1710 | * indicates there is a contiguous sequence present) | ||
1711 | * - discard any incomplete sequences that are below minseq -- due | ||
1712 | * to the fact that sender always increment sequence number, if there | ||
1713 | * is an incomplete sequence below minseq, no new fragments would | ||
1714 | * come to complete such sequence and it should be discarded | ||
1715 | * | ||
1716 | * loop completes when we accomplished the following tasks: | ||
1717 | * - new fragment is inserted in the proper sequence ('newfrag' is | ||
1718 | * set to NULL) | ||
1719 | * - we hit a gap in the sequence, so no reassembly/processing is | ||
1720 | * possible ('start' would be set to NULL) | ||
1721 | * | ||
1722 | * algorithm for this code is derived from code in the book | ||
1723 | * 'PPP Design And Debugging' by James Carlson (Addison-Wesley) | ||
1724 | */ | ||
1725 | while (start != NULL || newfrag != NULL) { | ||
1726 | |||
1727 | thisseq = MP_SEQ(frag); | ||
1728 | nextf = frag->next; | ||
1729 | |||
1730 | /* drop any duplicate fragments */ | ||
1731 | if (newfrag != NULL && thisseq == newseq) { | ||
1732 | isdn_ppp_mp_free_skb(mp, newfrag); | ||
1733 | newfrag = NULL; | ||
1734 | } | ||
1735 | |||
1736 | /* insert new fragment before next element if possible. */ | ||
1737 | if (newfrag != NULL && (nextf == NULL || | ||
1738 | MP_LT(newseq, MP_SEQ(nextf)))) { | ||
1739 | newfrag->next = nextf; | ||
1740 | frag->next = nextf = newfrag; | ||
1741 | newfrag = NULL; | ||
1742 | } | ||
1743 | |||
1744 | if (start != NULL) { | ||
1745 | /* check for misplaced start */ | ||
1746 | if (start != frag && (MP_FLAGS(frag) & MP_BEGIN_FRAG)) { | ||
1747 | printk(KERN_WARNING"isdn_mppp(seq %d): new " | ||
1748 | "BEGIN flag with no prior END", thisseq); | ||
1749 | stats->seqerrs++; | ||
1750 | stats->frame_drops++; | ||
1751 | start = isdn_ppp_mp_discard(mp, start, frag); | ||
1752 | nextf = frag->next; | ||
1753 | } | ||
1754 | } else if (MP_LE(thisseq, minseq)) { | ||
1755 | if (MP_FLAGS(frag) & MP_BEGIN_FRAG) | ||
1756 | start = frag; | ||
1757 | else { | ||
1758 | if (MP_FLAGS(frag) & MP_END_FRAG) | ||
1759 | stats->frame_drops++; | ||
1760 | if (mp->frags == frag) | ||
1761 | mp->frags = nextf; | ||
1762 | isdn_ppp_mp_free_skb(mp, frag); | ||
1763 | frag = nextf; | ||
1764 | continue; | ||
1765 | } | ||
1766 | } | ||
1767 | |||
1768 | /* if start is non-null and we have end fragment, then | ||
1769 | * we have full reassembly sequence -- reassemble | ||
1770 | * and process packet now | ||
1771 | */ | ||
1772 | if (start != NULL && (MP_FLAGS(frag) & MP_END_FRAG)) { | ||
1773 | minseq = mp->seq = (thisseq + 1) & MP_LONGSEQ_MASK; | ||
1774 | /* Reassemble the packet then dispatch it */ | ||
1775 | isdn_ppp_mp_reassembly(net_dev, lp, start, nextf); | ||
1776 | |||
1777 | start = NULL; | ||
1778 | frag = NULL; | ||
1779 | |||
1780 | mp->frags = nextf; | ||
1781 | } | ||
1782 | |||
1783 | /* check if need to update start pointer: if we just | ||
1784 | * reassembled the packet and sequence is contiguous | ||
1785 | * then next fragment should be the start of new reassembly | ||
1786 | * if sequence is contiguous, but we haven't reassembled yet, | ||
1787 | * keep going. | ||
1788 | * if sequence is not contiguous, either clear everything | ||
1789 | * below low watermark and set start to the next frag or | ||
1790 | * clear start ptr. | ||
1791 | */ | ||
1792 | if (nextf != NULL && | ||
1793 | ((thisseq + 1) & MP_LONGSEQ_MASK) == MP_SEQ(nextf)) { | ||
1794 | /* if we just reassembled and the next one is here, | ||
1795 | * then start another reassembly. */ | ||
1796 | |||
1797 | if (frag == NULL) { | ||
1798 | if (MP_FLAGS(nextf) & MP_BEGIN_FRAG) | ||
1799 | start = nextf; | ||
1800 | else | ||
1801 | { | ||
1802 | printk(KERN_WARNING"isdn_mppp(seq %d):" | ||
1803 | " END flag with no following " | ||
1804 | "BEGIN", thisseq); | ||
1805 | stats->seqerrs++; | ||
1806 | } | ||
1807 | } | ||
1808 | |||
1809 | } else { | ||
1810 | if (nextf != NULL && frag != NULL && | ||
1811 | MP_LT(thisseq, minseq)) { | ||
1812 | /* we've got a break in the sequence | ||
1813 | * and we not at the end yet | ||
1814 | * and we did not just reassembled | ||
1815 | *(if we did, there wouldn't be anything before) | ||
1816 | * and we below the low watermark | ||
1817 | * discard all the frames below low watermark | ||
1818 | * and start over */ | ||
1819 | stats->frame_drops++; | ||
1820 | mp->frags = isdn_ppp_mp_discard(mp, start, nextf); | ||
1821 | } | ||
1822 | /* break in the sequence, no reassembly */ | ||
1823 | start = NULL; | ||
1824 | } | ||
1825 | |||
1826 | frag = nextf; | ||
1827 | } /* while -- main loop */ | ||
1828 | |||
1829 | if (mp->frags == NULL) | ||
1830 | mp->frags = frag; | ||
1831 | |||
1832 | /* rather straighforward way to deal with (not very) possible | ||
1833 | * queue overflow */ | ||
1834 | if (mp->frames > MP_MAX_QUEUE_LEN) { | ||
1835 | stats->overflows++; | ||
1836 | while (mp->frames > MP_MAX_QUEUE_LEN) { | ||
1837 | frag = mp->frags->next; | ||
1838 | isdn_ppp_mp_free_skb(mp, mp->frags); | ||
1839 | mp->frags = frag; | ||
1840 | } | ||
1841 | } | ||
1842 | spin_unlock_irqrestore(&mp->lock, flags); | ||
1843 | } | ||
1844 | |||
1845 | static void isdn_ppp_mp_cleanup(isdn_net_local *lp) | ||
1846 | { | ||
1847 | struct sk_buff *frag = lp->netdev->pb->frags; | ||
1848 | struct sk_buff *nextfrag; | ||
1849 | while (frag) { | ||
1850 | nextfrag = frag->next; | ||
1851 | isdn_ppp_mp_free_skb(lp->netdev->pb, frag); | ||
1852 | frag = nextfrag; | ||
1853 | } | ||
1854 | lp->netdev->pb->frags = NULL; | ||
1855 | } | ||
1856 | |||
1857 | static u32 isdn_ppp_mp_get_seq(int short_seq, | ||
1858 | struct sk_buff *skb, u32 last_seq) | ||
1859 | { | ||
1860 | u32 seq; | ||
1861 | int flags = skb->data[0] & (MP_BEGIN_FRAG | MP_END_FRAG); | ||
1862 | |||
1863 | if (!short_seq) | ||
1864 | { | ||
1865 | seq = ntohl(*(__be32 *)skb->data) & MP_LONGSEQ_MASK; | ||
1866 | skb_push(skb, 1); | ||
1867 | } | ||
1868 | else | ||
1869 | { | ||
1870 | /* convert 12-bit short seq number to 24-bit long one | ||
1871 | */ | ||
1872 | seq = ntohs(*(__be16 *)skb->data) & MP_SHORTSEQ_MASK; | ||
1873 | |||
1874 | /* check for seqence wrap */ | ||
1875 | if (!(seq & MP_SHORTSEQ_MAXBIT) && | ||
1876 | (last_seq & MP_SHORTSEQ_MAXBIT) && | ||
1877 | (unsigned long)last_seq <= MP_LONGSEQ_MAX) | ||
1878 | seq |= (last_seq + MP_SHORTSEQ_MAX + 1) & | ||
1879 | (~MP_SHORTSEQ_MASK & MP_LONGSEQ_MASK); | ||
1880 | else | ||
1881 | seq |= last_seq & (~MP_SHORTSEQ_MASK & MP_LONGSEQ_MASK); | ||
1882 | |||
1883 | skb_push(skb, 3); /* put converted seqence back in skb */ | ||
1884 | } | ||
1885 | *(u32 *)(skb->data + 1) = seq; /* put seqence back in _host_ byte | ||
1886 | * order */ | ||
1887 | skb->data[0] = flags; /* restore flags */ | ||
1888 | return seq; | ||
1889 | } | ||
1890 | |||
1891 | static struct sk_buff *isdn_ppp_mp_discard(ippp_bundle *mp, | ||
1892 | struct sk_buff *from, | ||
1893 | struct sk_buff *to) | ||
1894 | { | ||
1895 | if (from) | ||
1896 | while (from != to) { | ||
1897 | struct sk_buff *next = from->next; | ||
1898 | isdn_ppp_mp_free_skb(mp, from); | ||
1899 | from = next; | ||
1900 | } | ||
1901 | return from; | ||
1902 | } | ||
1903 | |||
1904 | static void isdn_ppp_mp_reassembly(isdn_net_dev *net_dev, isdn_net_local *lp, | ||
1905 | struct sk_buff *from, struct sk_buff *to) | ||
1906 | { | ||
1907 | ippp_bundle *mp = net_dev->pb; | ||
1908 | int proto; | ||
1909 | struct sk_buff *skb; | ||
1910 | unsigned int tot_len; | ||
1911 | |||
1912 | if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) { | ||
1913 | printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n", | ||
1914 | __func__, lp->ppp_slot); | ||
1915 | return; | ||
1916 | } | ||
1917 | if (MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG)) { | ||
1918 | if (ippp_table[lp->ppp_slot]->debug & 0x40) | ||
1919 | printk(KERN_DEBUG "isdn_mppp: reassembly: frame %d, " | ||
1920 | "len %d\n", MP_SEQ(from), from->len); | ||
1921 | skb = from; | ||
1922 | skb_pull(skb, MP_HEADER_LEN); | ||
1923 | mp->frames--; | ||
1924 | } else { | ||
1925 | struct sk_buff *frag; | ||
1926 | int n; | ||
1927 | |||
1928 | for (tot_len = n = 0, frag = from; frag != to; frag = frag->next, n++) | ||
1929 | tot_len += frag->len - MP_HEADER_LEN; | ||
1930 | |||
1931 | if (ippp_table[lp->ppp_slot]->debug & 0x40) | ||
1932 | printk(KERN_DEBUG"isdn_mppp: reassembling frames %d " | ||
1933 | "to %d, len %d\n", MP_SEQ(from), | ||
1934 | (MP_SEQ(from) + n - 1) & MP_LONGSEQ_MASK, tot_len); | ||
1935 | if ((skb = dev_alloc_skb(tot_len)) == NULL) { | ||
1936 | printk(KERN_ERR "isdn_mppp: cannot allocate sk buff " | ||
1937 | "of size %d\n", tot_len); | ||
1938 | isdn_ppp_mp_discard(mp, from, to); | ||
1939 | return; | ||
1940 | } | ||
1941 | |||
1942 | while (from != to) { | ||
1943 | unsigned int len = from->len - MP_HEADER_LEN; | ||
1944 | |||
1945 | skb_copy_from_linear_data_offset(from, MP_HEADER_LEN, | ||
1946 | skb_put(skb, len), | ||
1947 | len); | ||
1948 | frag = from->next; | ||
1949 | isdn_ppp_mp_free_skb(mp, from); | ||
1950 | from = frag; | ||
1951 | } | ||
1952 | } | ||
1953 | proto = isdn_ppp_strip_proto(skb); | ||
1954 | isdn_ppp_push_higher(net_dev, lp, skb, proto); | ||
1955 | } | ||
1956 | |||
1957 | static void isdn_ppp_mp_free_skb(ippp_bundle *mp, struct sk_buff *skb) | ||
1958 | { | ||
1959 | dev_kfree_skb(skb); | ||
1960 | mp->frames--; | ||
1961 | } | ||
1962 | |||
1963 | static void isdn_ppp_mp_print_recv_pkt(int slot, struct sk_buff *skb) | ||
1964 | { | ||
1965 | printk(KERN_DEBUG "mp_recv: %d/%d -> %02x %02x %02x %02x %02x %02x\n", | ||
1966 | slot, (int) skb->len, | ||
1967 | (int) skb->data[0], (int) skb->data[1], (int) skb->data[2], | ||
1968 | (int) skb->data[3], (int) skb->data[4], (int) skb->data[5]); | ||
1969 | } | ||
1970 | |||
1971 | static int | ||
1972 | isdn_ppp_bundle(struct ippp_struct *is, int unit) | ||
1973 | { | ||
1974 | char ifn[IFNAMSIZ + 1]; | ||
1975 | isdn_net_dev *p; | ||
1976 | isdn_net_local *lp, *nlp; | ||
1977 | int rc; | ||
1978 | unsigned long flags; | ||
1979 | |||
1980 | sprintf(ifn, "ippp%d", unit); | ||
1981 | p = isdn_net_findif(ifn); | ||
1982 | if (!p) { | ||
1983 | printk(KERN_ERR "ippp_bundle: cannot find %s\n", ifn); | ||
1984 | return -EINVAL; | ||
1985 | } | ||
1986 | |||
1987 | spin_lock_irqsave(&p->pb->lock, flags); | ||
1988 | |||
1989 | nlp = is->lp; | ||
1990 | lp = p->queue; | ||
1991 | if (nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS || | ||
1992 | lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) { | ||
1993 | printk(KERN_ERR "ippp_bundle: binding to invalid slot %d\n", | ||
1994 | nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ? | ||
1995 | nlp->ppp_slot : lp->ppp_slot); | ||
1996 | rc = -EINVAL; | ||
1997 | goto out; | ||
1998 | } | ||
1999 | |||
2000 | isdn_net_add_to_bundle(p, nlp); | ||
2001 | |||
2002 | ippp_table[nlp->ppp_slot]->unit = ippp_table[lp->ppp_slot]->unit; | ||
2003 | |||
2004 | /* maybe also SC_CCP stuff */ | ||
2005 | ippp_table[nlp->ppp_slot]->pppcfg |= ippp_table[lp->ppp_slot]->pppcfg & | ||
2006 | (SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP); | ||
2007 | ippp_table[nlp->ppp_slot]->mpppcfg |= ippp_table[lp->ppp_slot]->mpppcfg & | ||
2008 | (SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ); | ||
2009 | rc = isdn_ppp_mp_init(nlp, p->pb); | ||
2010 | out: | ||
2011 | spin_unlock_irqrestore(&p->pb->lock, flags); | ||
2012 | return rc; | ||
2013 | } | ||
2014 | |||
2015 | #endif /* CONFIG_ISDN_MPP */ | ||
2016 | |||
2017 | /* | ||
2018 | * network device ioctl handlers | ||
2019 | */ | ||
2020 | |||
2021 | static int | ||
2022 | isdn_ppp_dev_ioctl_stats(int slot, struct ifreq *ifr, struct net_device *dev) | ||
2023 | { | ||
2024 | struct ppp_stats __user *res = ifr->ifr_data; | ||
2025 | struct ppp_stats t; | ||
2026 | isdn_net_local *lp = netdev_priv(dev); | ||
2027 | |||
2028 | /* build a temporary stat struct and copy it to user space */ | ||
2029 | |||
2030 | memset(&t, 0, sizeof(struct ppp_stats)); | ||
2031 | if (dev->flags & IFF_UP) { | ||
2032 | t.p.ppp_ipackets = lp->stats.rx_packets; | ||
2033 | t.p.ppp_ibytes = lp->stats.rx_bytes; | ||
2034 | t.p.ppp_ierrors = lp->stats.rx_errors; | ||
2035 | t.p.ppp_opackets = lp->stats.tx_packets; | ||
2036 | t.p.ppp_obytes = lp->stats.tx_bytes; | ||
2037 | t.p.ppp_oerrors = lp->stats.tx_errors; | ||
2038 | #ifdef CONFIG_ISDN_PPP_VJ | ||
2039 | if (slot >= 0 && ippp_table[slot]->slcomp) { | ||
2040 | struct slcompress *slcomp = ippp_table[slot]->slcomp; | ||
2041 | t.vj.vjs_packets = slcomp->sls_o_compressed + slcomp->sls_o_uncompressed; | ||
2042 | t.vj.vjs_compressed = slcomp->sls_o_compressed; | ||
2043 | t.vj.vjs_searches = slcomp->sls_o_searches; | ||
2044 | t.vj.vjs_misses = slcomp->sls_o_misses; | ||
2045 | t.vj.vjs_errorin = slcomp->sls_i_error; | ||
2046 | t.vj.vjs_tossed = slcomp->sls_i_tossed; | ||
2047 | t.vj.vjs_uncompressedin = slcomp->sls_i_uncompressed; | ||
2048 | t.vj.vjs_compressedin = slcomp->sls_i_compressed; | ||
2049 | } | ||
2050 | #endif | ||
2051 | } | ||
2052 | if (copy_to_user(res, &t, sizeof(struct ppp_stats))) | ||
2053 | return -EFAULT; | ||
2054 | return 0; | ||
2055 | } | ||
2056 | |||
2057 | int | ||
2058 | isdn_ppp_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | ||
2059 | { | ||
2060 | int error = 0; | ||
2061 | int len; | ||
2062 | isdn_net_local *lp = netdev_priv(dev); | ||
2063 | |||
2064 | |||
2065 | if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) | ||
2066 | return -EINVAL; | ||
2067 | |||
2068 | switch (cmd) { | ||
2069 | #define PPP_VERSION "2.3.7" | ||
2070 | case SIOCGPPPVER: | ||
2071 | len = strlen(PPP_VERSION) + 1; | ||
2072 | if (copy_to_user(ifr->ifr_data, PPP_VERSION, len)) | ||
2073 | error = -EFAULT; | ||
2074 | break; | ||
2075 | |||
2076 | case SIOCGPPPSTATS: | ||
2077 | error = isdn_ppp_dev_ioctl_stats(lp->ppp_slot, ifr, dev); | ||
2078 | break; | ||
2079 | default: | ||
2080 | error = -EINVAL; | ||
2081 | break; | ||
2082 | } | ||
2083 | return error; | ||
2084 | } | ||
2085 | |||
2086 | static int | ||
2087 | isdn_ppp_if_get_unit(char *name) | ||
2088 | { | ||
2089 | int len, | ||
2090 | i, | ||
2091 | unit = 0, | ||
2092 | deci; | ||
2093 | |||
2094 | len = strlen(name); | ||
2095 | |||
2096 | if (strncmp("ippp", name, 4) || len > 8) | ||
2097 | return -1; | ||
2098 | |||
2099 | for (i = 0, deci = 1; i < len; i++, deci *= 10) { | ||
2100 | char a = name[len - i - 1]; | ||
2101 | if (a >= '0' && a <= '9') | ||
2102 | unit += (a - '0') * deci; | ||
2103 | else | ||
2104 | break; | ||
2105 | } | ||
2106 | if (!i || len - i != 4) | ||
2107 | unit = -1; | ||
2108 | |||
2109 | return unit; | ||
2110 | } | ||
2111 | |||
2112 | |||
2113 | int | ||
2114 | isdn_ppp_dial_slave(char *name) | ||
2115 | { | ||
2116 | #ifdef CONFIG_ISDN_MPP | ||
2117 | isdn_net_dev *ndev; | ||
2118 | isdn_net_local *lp; | ||
2119 | struct net_device *sdev; | ||
2120 | |||
2121 | if (!(ndev = isdn_net_findif(name))) | ||
2122 | return 1; | ||
2123 | lp = ndev->local; | ||
2124 | if (!(lp->flags & ISDN_NET_CONNECTED)) | ||
2125 | return 5; | ||
2126 | |||
2127 | sdev = lp->slave; | ||
2128 | while (sdev) { | ||
2129 | isdn_net_local *mlp = netdev_priv(sdev); | ||
2130 | if (!(mlp->flags & ISDN_NET_CONNECTED)) | ||
2131 | break; | ||
2132 | sdev = mlp->slave; | ||
2133 | } | ||
2134 | if (!sdev) | ||
2135 | return 2; | ||
2136 | |||
2137 | isdn_net_dial_req(netdev_priv(sdev)); | ||
2138 | return 0; | ||
2139 | #else | ||
2140 | return -1; | ||
2141 | #endif | ||
2142 | } | ||
2143 | |||
2144 | int | ||
2145 | isdn_ppp_hangup_slave(char *name) | ||
2146 | { | ||
2147 | #ifdef CONFIG_ISDN_MPP | ||
2148 | isdn_net_dev *ndev; | ||
2149 | isdn_net_local *lp; | ||
2150 | struct net_device *sdev; | ||
2151 | |||
2152 | if (!(ndev = isdn_net_findif(name))) | ||
2153 | return 1; | ||
2154 | lp = ndev->local; | ||
2155 | if (!(lp->flags & ISDN_NET_CONNECTED)) | ||
2156 | return 5; | ||
2157 | |||
2158 | sdev = lp->slave; | ||
2159 | while (sdev) { | ||
2160 | isdn_net_local *mlp = netdev_priv(sdev); | ||
2161 | |||
2162 | if (mlp->slave) { /* find last connected link in chain */ | ||
2163 | isdn_net_local *nlp = ISDN_SLAVE_PRIV(mlp); | ||
2164 | |||
2165 | if (!(nlp->flags & ISDN_NET_CONNECTED)) | ||
2166 | break; | ||
2167 | } else if (mlp->flags & ISDN_NET_CONNECTED) | ||
2168 | break; | ||
2169 | |||
2170 | sdev = mlp->slave; | ||
2171 | } | ||
2172 | if (!sdev) | ||
2173 | return 2; | ||
2174 | |||
2175 | isdn_net_hangup(sdev); | ||
2176 | return 0; | ||
2177 | #else | ||
2178 | return -1; | ||
2179 | #endif | ||
2180 | } | ||
2181 | |||
2182 | /* | ||
2183 | * PPP compression stuff | ||
2184 | */ | ||
2185 | |||
2186 | |||
2187 | /* Push an empty CCP Data Frame up to the daemon to wake it up and let it | ||
2188 | generate a CCP Reset-Request or tear down CCP altogether */ | ||
2189 | |||
2190 | static void isdn_ppp_ccp_kickup(struct ippp_struct *is) | ||
2191 | { | ||
2192 | isdn_ppp_fill_rq(NULL, 0, PPP_COMP, is->lp->ppp_slot); | ||
2193 | } | ||
2194 | |||
2195 | /* In-kernel handling of CCP Reset-Request and Reset-Ack is necessary, | ||
2196 | but absolutely nontrivial. The most abstruse problem we are facing is | ||
2197 | that the generation, reception and all the handling of timeouts and | ||
2198 | resends including proper request id management should be entirely left | ||
2199 | to the (de)compressor, but indeed is not covered by the current API to | ||
2200 | the (de)compressor. The API is a prototype version from PPP where only | ||
2201 | some (de)compressors have yet been implemented and all of them are | ||
2202 | rather simple in their reset handling. Especially, their is only one | ||
2203 | outstanding ResetAck at a time with all of them and ResetReq/-Acks do | ||
2204 | not have parameters. For this very special case it was sufficient to | ||
2205 | just return an error code from the decompressor and have a single | ||
2206 | reset() entry to communicate all the necessary information between | ||
2207 | the framework and the (de)compressor. Bad enough, LZS is different | ||
2208 | (and any other compressor may be different, too). It has multiple | ||
2209 | histories (eventually) and needs to Reset each of them independently | ||
2210 | and thus uses multiple outstanding Acks and history numbers as an | ||
2211 | additional parameter to Reqs/Acks. | ||
2212 | All that makes it harder to port the reset state engine into the | ||
2213 | kernel because it is not just the same simple one as in (i)pppd but | ||
2214 | it must be able to pass additional parameters and have multiple out- | ||
2215 | standing Acks. We are trying to achieve the impossible by handling | ||
2216 | reset transactions independent by their id. The id MUST change when | ||
2217 | the data portion changes, thus any (de)compressor who uses more than | ||
2218 | one resettable state must provide and recognize individual ids for | ||
2219 | each individual reset transaction. The framework itself does _only_ | ||
2220 | differentiate them by id, because it has no other semantics like the | ||
2221 | (de)compressor might. | ||
2222 | This looks like a major redesign of the interface would be nice, | ||
2223 | but I don't have an idea how to do it better. */ | ||
2224 | |||
2225 | /* Send a CCP Reset-Request or Reset-Ack directly from the kernel. This is | ||
2226 | getting that lengthy because there is no simple "send-this-frame-out" | ||
2227 | function above but every wrapper does a bit different. Hope I guess | ||
2228 | correct in this hack... */ | ||
2229 | |||
2230 | static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto, | ||
2231 | unsigned char code, unsigned char id, | ||
2232 | unsigned char *data, int len) | ||
2233 | { | ||
2234 | struct sk_buff *skb; | ||
2235 | unsigned char *p; | ||
2236 | int hl; | ||
2237 | int cnt = 0; | ||
2238 | isdn_net_local *lp = is->lp; | ||
2239 | |||
2240 | /* Alloc large enough skb */ | ||
2241 | hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; | ||
2242 | skb = alloc_skb(len + hl + 16, GFP_ATOMIC); | ||
2243 | if (!skb) { | ||
2244 | printk(KERN_WARNING | ||
2245 | "ippp: CCP cannot send reset - out of memory\n"); | ||
2246 | return; | ||
2247 | } | ||
2248 | skb_reserve(skb, hl); | ||
2249 | |||
2250 | /* We may need to stuff an address and control field first */ | ||
2251 | if (!(is->pppcfg & SC_COMP_AC)) { | ||
2252 | p = skb_put(skb, 2); | ||
2253 | *p++ = 0xff; | ||
2254 | *p++ = 0x03; | ||
2255 | } | ||
2256 | |||
2257 | /* Stuff proto, code, id and length */ | ||
2258 | p = skb_put(skb, 6); | ||
2259 | *p++ = (proto >> 8); | ||
2260 | *p++ = (proto & 0xff); | ||
2261 | *p++ = code; | ||
2262 | *p++ = id; | ||
2263 | cnt = 4 + len; | ||
2264 | *p++ = (cnt >> 8); | ||
2265 | *p++ = (cnt & 0xff); | ||
2266 | |||
2267 | /* Now stuff remaining bytes */ | ||
2268 | if (len) { | ||
2269 | skb_put_data(skb, data, len); | ||
2270 | } | ||
2271 | |||
2272 | /* skb is now ready for xmit */ | ||
2273 | printk(KERN_DEBUG "Sending CCP Frame:\n"); | ||
2274 | isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit, lp->ppp_slot); | ||
2275 | |||
2276 | isdn_net_write_super(lp, skb); | ||
2277 | } | ||
2278 | |||
2279 | /* Allocate the reset state vector */ | ||
2280 | static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is) | ||
2281 | { | ||
2282 | struct ippp_ccp_reset *r; | ||
2283 | r = kzalloc(sizeof(struct ippp_ccp_reset), GFP_KERNEL); | ||
2284 | if (!r) { | ||
2285 | printk(KERN_ERR "ippp_ccp: failed to allocate reset data" | ||
2286 | " structure - no mem\n"); | ||
2287 | return NULL; | ||
2288 | } | ||
2289 | printk(KERN_DEBUG "ippp_ccp: allocated reset data structure %p\n", r); | ||
2290 | is->reset = r; | ||
2291 | return r; | ||
2292 | } | ||
2293 | |||
2294 | /* Destroy the reset state vector. Kill all pending timers first. */ | ||
2295 | static void isdn_ppp_ccp_reset_free(struct ippp_struct *is) | ||
2296 | { | ||
2297 | unsigned int id; | ||
2298 | |||
2299 | printk(KERN_DEBUG "ippp_ccp: freeing reset data structure %p\n", | ||
2300 | is->reset); | ||
2301 | for (id = 0; id < 256; id++) { | ||
2302 | if (is->reset->rs[id]) { | ||
2303 | isdn_ppp_ccp_reset_free_state(is, (unsigned char)id); | ||
2304 | } | ||
2305 | } | ||
2306 | kfree(is->reset); | ||
2307 | is->reset = NULL; | ||
2308 | } | ||
2309 | |||
2310 | /* Free a given state and clear everything up for later reallocation */ | ||
2311 | static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is, | ||
2312 | unsigned char id) | ||
2313 | { | ||
2314 | struct ippp_ccp_reset_state *rs; | ||
2315 | |||
2316 | if (is->reset->rs[id]) { | ||
2317 | printk(KERN_DEBUG "ippp_ccp: freeing state for id %d\n", id); | ||
2318 | rs = is->reset->rs[id]; | ||
2319 | /* Make sure the kernel will not call back later */ | ||
2320 | if (rs->ta) | ||
2321 | del_timer(&rs->timer); | ||
2322 | is->reset->rs[id] = NULL; | ||
2323 | kfree(rs); | ||
2324 | } else { | ||
2325 | printk(KERN_WARNING "ippp_ccp: id %d is not allocated\n", id); | ||
2326 | } | ||
2327 | } | ||
2328 | |||
2329 | /* The timer callback function which is called when a ResetReq has timed out, | ||
2330 | aka has never been answered by a ResetAck */ | ||
2331 | static void isdn_ppp_ccp_timer_callback(struct timer_list *t) | ||
2332 | { | ||
2333 | struct ippp_ccp_reset_state *rs = | ||
2334 | from_timer(rs, t, timer); | ||
2335 | |||
2336 | if (!rs) { | ||
2337 | printk(KERN_ERR "ippp_ccp: timer cb with zero closure.\n"); | ||
2338 | return; | ||
2339 | } | ||
2340 | if (rs->ta && rs->state == CCPResetSentReq) { | ||
2341 | /* We are correct here */ | ||
2342 | if (!rs->expra) { | ||
2343 | /* Hmm, there is no Ack really expected. We can clean | ||
2344 | up the state now, it will be reallocated if the | ||
2345 | decompressor insists on another reset */ | ||
2346 | rs->ta = 0; | ||
2347 | isdn_ppp_ccp_reset_free_state(rs->is, rs->id); | ||
2348 | return; | ||
2349 | } | ||
2350 | printk(KERN_DEBUG "ippp_ccp: CCP Reset timed out for id %d\n", | ||
2351 | rs->id); | ||
2352 | /* Push it again */ | ||
2353 | isdn_ppp_ccp_xmit_reset(rs->is, PPP_CCP, CCP_RESETREQ, rs->id, | ||
2354 | rs->data, rs->dlen); | ||
2355 | /* Restart timer */ | ||
2356 | rs->timer.expires = jiffies + HZ * 5; | ||
2357 | add_timer(&rs->timer); | ||
2358 | } else { | ||
2359 | printk(KERN_WARNING "ippp_ccp: timer cb in wrong state %d\n", | ||
2360 | rs->state); | ||
2361 | } | ||
2362 | } | ||
2363 | |||
2364 | /* Allocate a new reset transaction state */ | ||
2365 | static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is, | ||
2366 | unsigned char id) | ||
2367 | { | ||
2368 | struct ippp_ccp_reset_state *rs; | ||
2369 | if (is->reset->rs[id]) { | ||
2370 | printk(KERN_WARNING "ippp_ccp: old state exists for id %d\n", | ||
2371 | id); | ||
2372 | return NULL; | ||
2373 | } else { | ||
2374 | rs = kzalloc(sizeof(struct ippp_ccp_reset_state), GFP_ATOMIC); | ||
2375 | if (!rs) | ||
2376 | return NULL; | ||
2377 | rs->state = CCPResetIdle; | ||
2378 | rs->is = is; | ||
2379 | rs->id = id; | ||
2380 | timer_setup(&rs->timer, isdn_ppp_ccp_timer_callback, 0); | ||
2381 | is->reset->rs[id] = rs; | ||
2382 | } | ||
2383 | return rs; | ||
2384 | } | ||
2385 | |||
2386 | |||
2387 | /* A decompressor wants a reset with a set of parameters - do what is | ||
2388 | necessary to fulfill it */ | ||
2389 | static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is, | ||
2390 | struct isdn_ppp_resetparams *rp) | ||
2391 | { | ||
2392 | struct ippp_ccp_reset_state *rs; | ||
2393 | |||
2394 | if (rp->valid) { | ||
2395 | /* The decompressor defines parameters by itself */ | ||
2396 | if (rp->rsend) { | ||
2397 | /* And he wants us to send a request */ | ||
2398 | if (!(rp->idval)) { | ||
2399 | printk(KERN_ERR "ippp_ccp: decompressor must" | ||
2400 | " specify reset id\n"); | ||
2401 | return; | ||
2402 | } | ||
2403 | if (is->reset->rs[rp->id]) { | ||
2404 | /* There is already a transaction in existence | ||
2405 | for this id. May be still waiting for a | ||
2406 | Ack or may be wrong. */ | ||
2407 | rs = is->reset->rs[rp->id]; | ||
2408 | if (rs->state == CCPResetSentReq && rs->ta) { | ||
2409 | printk(KERN_DEBUG "ippp_ccp: reset" | ||
2410 | " trans still in progress" | ||
2411 | " for id %d\n", rp->id); | ||
2412 | } else { | ||
2413 | printk(KERN_WARNING "ippp_ccp: reset" | ||
2414 | " trans in wrong state %d for" | ||
2415 | " id %d\n", rs->state, rp->id); | ||
2416 | } | ||
2417 | } else { | ||
2418 | /* Ok, this is a new transaction */ | ||
2419 | printk(KERN_DEBUG "ippp_ccp: new trans for id" | ||
2420 | " %d to be started\n", rp->id); | ||
2421 | rs = isdn_ppp_ccp_reset_alloc_state(is, rp->id); | ||
2422 | if (!rs) { | ||
2423 | printk(KERN_ERR "ippp_ccp: out of mem" | ||
2424 | " allocing ccp trans\n"); | ||
2425 | return; | ||
2426 | } | ||
2427 | rs->state = CCPResetSentReq; | ||
2428 | rs->expra = rp->expra; | ||
2429 | if (rp->dtval) { | ||
2430 | rs->dlen = rp->dlen; | ||
2431 | memcpy(rs->data, rp->data, rp->dlen); | ||
2432 | } | ||
2433 | /* HACK TODO - add link comp here */ | ||
2434 | isdn_ppp_ccp_xmit_reset(is, PPP_CCP, | ||
2435 | CCP_RESETREQ, rs->id, | ||
2436 | rs->data, rs->dlen); | ||
2437 | /* Start the timer */ | ||
2438 | rs->timer.expires = jiffies + 5 * HZ; | ||
2439 | add_timer(&rs->timer); | ||
2440 | rs->ta = 1; | ||
2441 | } | ||
2442 | } else { | ||
2443 | printk(KERN_DEBUG "ippp_ccp: no reset sent\n"); | ||
2444 | } | ||
2445 | } else { | ||
2446 | /* The reset params are invalid. The decompressor does not | ||
2447 | care about them, so we just send the minimal requests | ||
2448 | and increase ids only when an Ack is received for a | ||
2449 | given id */ | ||
2450 | if (is->reset->rs[is->reset->lastid]) { | ||
2451 | /* There is already a transaction in existence | ||
2452 | for this id. May be still waiting for a | ||
2453 | Ack or may be wrong. */ | ||
2454 | rs = is->reset->rs[is->reset->lastid]; | ||
2455 | if (rs->state == CCPResetSentReq && rs->ta) { | ||
2456 | printk(KERN_DEBUG "ippp_ccp: reset" | ||
2457 | " trans still in progress" | ||
2458 | " for id %d\n", rp->id); | ||
2459 | } else { | ||
2460 | printk(KERN_WARNING "ippp_ccp: reset" | ||
2461 | " trans in wrong state %d for" | ||
2462 | " id %d\n", rs->state, rp->id); | ||
2463 | } | ||
2464 | } else { | ||
2465 | printk(KERN_DEBUG "ippp_ccp: new trans for id" | ||
2466 | " %d to be started\n", is->reset->lastid); | ||
2467 | rs = isdn_ppp_ccp_reset_alloc_state(is, | ||
2468 | is->reset->lastid); | ||
2469 | if (!rs) { | ||
2470 | printk(KERN_ERR "ippp_ccp: out of mem" | ||
2471 | " allocing ccp trans\n"); | ||
2472 | return; | ||
2473 | } | ||
2474 | rs->state = CCPResetSentReq; | ||
2475 | /* We always expect an Ack if the decompressor doesn't | ||
2476 | know better */ | ||
2477 | rs->expra = 1; | ||
2478 | rs->dlen = 0; | ||
2479 | /* HACK TODO - add link comp here */ | ||
2480 | isdn_ppp_ccp_xmit_reset(is, PPP_CCP, CCP_RESETREQ, | ||
2481 | rs->id, NULL, 0); | ||
2482 | /* Start the timer */ | ||
2483 | rs->timer.expires = jiffies + 5 * HZ; | ||
2484 | add_timer(&rs->timer); | ||
2485 | rs->ta = 1; | ||
2486 | } | ||
2487 | } | ||
2488 | } | ||
2489 | |||
2490 | /* An Ack was received for this id. This means we stop the timer and clean | ||
2491 | up the state prior to calling the decompressors reset routine. */ | ||
2492 | static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is, | ||
2493 | unsigned char id) | ||
2494 | { | ||
2495 | struct ippp_ccp_reset_state *rs = is->reset->rs[id]; | ||
2496 | |||
2497 | if (rs) { | ||
2498 | if (rs->ta && rs->state == CCPResetSentReq) { | ||
2499 | /* Great, we are correct */ | ||
2500 | if (!rs->expra) | ||
2501 | printk(KERN_DEBUG "ippp_ccp: ResetAck received" | ||
2502 | " for id %d but not expected\n", id); | ||
2503 | } else { | ||
2504 | printk(KERN_INFO "ippp_ccp: ResetAck received out of" | ||
2505 | "sync for id %d\n", id); | ||
2506 | } | ||
2507 | if (rs->ta) { | ||
2508 | rs->ta = 0; | ||
2509 | del_timer(&rs->timer); | ||
2510 | } | ||
2511 | isdn_ppp_ccp_reset_free_state(is, id); | ||
2512 | } else { | ||
2513 | printk(KERN_INFO "ippp_ccp: ResetAck received for unknown id" | ||
2514 | " %d\n", id); | ||
2515 | } | ||
2516 | /* Make sure the simple reset stuff uses a new id next time */ | ||
2517 | is->reset->lastid++; | ||
2518 | } | ||
2519 | |||
2520 | /* | ||
2521 | * decompress packet | ||
2522 | * | ||
2523 | * if master = 0, we're trying to uncompress an per-link compressed packet, | ||
2524 | * as opposed to an compressed reconstructed-from-MPPP packet. | ||
2525 | * proto is updated to protocol field of uncompressed packet. | ||
2526 | * | ||
2527 | * retval: decompressed packet, | ||
2528 | * same packet if uncompressed, | ||
2529 | * NULL if decompression error | ||
2530 | */ | ||
2531 | |||
2532 | static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb, struct ippp_struct *is, struct ippp_struct *master, | ||
2533 | int *proto) | ||
2534 | { | ||
2535 | void *stat = NULL; | ||
2536 | struct isdn_ppp_compressor *ipc = NULL; | ||
2537 | struct sk_buff *skb_out; | ||
2538 | int len; | ||
2539 | struct ippp_struct *ri; | ||
2540 | struct isdn_ppp_resetparams rsparm; | ||
2541 | unsigned char rsdata[IPPP_RESET_MAXDATABYTES]; | ||
2542 | |||
2543 | if (!master) { | ||
2544 | // per-link decompression | ||
2545 | stat = is->link_decomp_stat; | ||
2546 | ipc = is->link_decompressor; | ||
2547 | ri = is; | ||
2548 | } else { | ||
2549 | stat = master->decomp_stat; | ||
2550 | ipc = master->decompressor; | ||
2551 | ri = master; | ||
2552 | } | ||
2553 | |||
2554 | if (!ipc) { | ||
2555 | // no decompressor -> we can't decompress. | ||
2556 | printk(KERN_DEBUG "ippp: no decompressor defined!\n"); | ||
2557 | return skb; | ||
2558 | } | ||
2559 | BUG_ON(!stat); // if we have a compressor, stat has been set as well | ||
2560 | |||
2561 | if ((master && *proto == PPP_COMP) || (!master && *proto == PPP_COMPFRAG)) { | ||
2562 | // compressed packets are compressed by their protocol type | ||
2563 | |||
2564 | // Set up reset params for the decompressor | ||
2565 | memset(&rsparm, 0, sizeof(rsparm)); | ||
2566 | rsparm.data = rsdata; | ||
2567 | rsparm.maxdlen = IPPP_RESET_MAXDATABYTES; | ||
2568 | |||
2569 | skb_out = dev_alloc_skb(is->mru + PPP_HDRLEN); | ||
2570 | if (!skb_out) { | ||
2571 | kfree_skb(skb); | ||
2572 | printk(KERN_ERR "ippp: decomp memory allocation failure\n"); | ||
2573 | return NULL; | ||
2574 | } | ||
2575 | len = ipc->decompress(stat, skb, skb_out, &rsparm); | ||
2576 | kfree_skb(skb); | ||
2577 | if (len <= 0) { | ||
2578 | switch (len) { | ||
2579 | case DECOMP_ERROR: | ||
2580 | printk(KERN_INFO "ippp: decomp wants reset %s params\n", | ||
2581 | rsparm.valid ? "with" : "without"); | ||
2582 | |||
2583 | isdn_ppp_ccp_reset_trans(ri, &rsparm); | ||
2584 | break; | ||
2585 | case DECOMP_FATALERROR: | ||
2586 | ri->pppcfg |= SC_DC_FERROR; | ||
2587 | /* Kick ipppd to recognize the error */ | ||
2588 | isdn_ppp_ccp_kickup(ri); | ||
2589 | break; | ||
2590 | } | ||
2591 | kfree_skb(skb_out); | ||
2592 | return NULL; | ||
2593 | } | ||
2594 | *proto = isdn_ppp_strip_proto(skb_out); | ||
2595 | if (*proto < 0) { | ||
2596 | kfree_skb(skb_out); | ||
2597 | return NULL; | ||
2598 | } | ||
2599 | return skb_out; | ||
2600 | } else { | ||
2601 | // uncompressed packets are fed through the decompressor to | ||
2602 | // update the decompressor state | ||
2603 | ipc->incomp(stat, skb, *proto); | ||
2604 | return skb; | ||
2605 | } | ||
2606 | } | ||
2607 | |||
2608 | /* | ||
2609 | * compress a frame | ||
2610 | * type=0: normal/bundle compression | ||
2611 | * =1: link compression | ||
2612 | * returns original skb if we haven't compressed the frame | ||
2613 | * and a new skb pointer if we've done it | ||
2614 | */ | ||
2615 | static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in, int *proto, | ||
2616 | struct ippp_struct *is, struct ippp_struct *master, int type) | ||
2617 | { | ||
2618 | int ret; | ||
2619 | int new_proto; | ||
2620 | struct isdn_ppp_compressor *compressor; | ||
2621 | void *stat; | ||
2622 | struct sk_buff *skb_out; | ||
2623 | |||
2624 | /* we do not compress control protocols */ | ||
2625 | if (*proto < 0 || *proto > 0x3fff) { | ||
2626 | return skb_in; | ||
2627 | } | ||
2628 | |||
2629 | if (type) { /* type=1 => Link compression */ | ||
2630 | return skb_in; | ||
2631 | } | ||
2632 | else { | ||
2633 | if (!master) { | ||
2634 | compressor = is->compressor; | ||
2635 | stat = is->comp_stat; | ||
2636 | } | ||
2637 | else { | ||
2638 | compressor = master->compressor; | ||
2639 | stat = master->comp_stat; | ||
2640 | } | ||
2641 | new_proto = PPP_COMP; | ||
2642 | } | ||
2643 | |||
2644 | if (!compressor) { | ||
2645 | printk(KERN_ERR "isdn_ppp: No compressor set!\n"); | ||
2646 | return skb_in; | ||
2647 | } | ||
2648 | if (!stat) { | ||
2649 | printk(KERN_ERR "isdn_ppp: Compressor not initialized?\n"); | ||
2650 | return skb_in; | ||
2651 | } | ||
2652 | |||
2653 | /* Allow for at least 150 % expansion (for now) */ | ||
2654 | skb_out = alloc_skb(skb_in->len + skb_in->len / 2 + 32 + | ||
2655 | skb_headroom(skb_in), GFP_ATOMIC); | ||
2656 | if (!skb_out) | ||
2657 | return skb_in; | ||
2658 | skb_reserve(skb_out, skb_headroom(skb_in)); | ||
2659 | |||
2660 | ret = (compressor->compress)(stat, skb_in, skb_out, *proto); | ||
2661 | if (!ret) { | ||
2662 | dev_kfree_skb(skb_out); | ||
2663 | return skb_in; | ||
2664 | } | ||
2665 | |||
2666 | dev_kfree_skb(skb_in); | ||
2667 | *proto = new_proto; | ||
2668 | return skb_out; | ||
2669 | } | ||
2670 | |||
2671 | /* | ||
2672 | * we received a CCP frame .. | ||
2673 | * not a clean solution, but we MUST handle a few cases in the kernel | ||
2674 | */ | ||
2675 | static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, | ||
2676 | struct sk_buff *skb, int proto) | ||
2677 | { | ||
2678 | struct ippp_struct *is; | ||
2679 | struct ippp_struct *mis; | ||
2680 | int len; | ||
2681 | struct isdn_ppp_resetparams rsparm; | ||
2682 | unsigned char rsdata[IPPP_RESET_MAXDATABYTES]; | ||
2683 | |||
2684 | printk(KERN_DEBUG "Received CCP frame from peer slot(%d)\n", | ||
2685 | lp->ppp_slot); | ||
2686 | if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) { | ||
2687 | printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n", | ||
2688 | __func__, lp->ppp_slot); | ||
2689 | return; | ||
2690 | } | ||
2691 | is = ippp_table[lp->ppp_slot]; | ||
2692 | isdn_ppp_frame_log("ccp-rcv", skb->data, skb->len, 32, is->unit, lp->ppp_slot); | ||
2693 | |||
2694 | if (lp->master) { | ||
2695 | int slot = ISDN_MASTER_PRIV(lp)->ppp_slot; | ||
2696 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
2697 | printk(KERN_ERR "%s: slot(%d) out of range\n", | ||
2698 | __func__, slot); | ||
2699 | return; | ||
2700 | } | ||
2701 | mis = ippp_table[slot]; | ||
2702 | } else | ||
2703 | mis = is; | ||
2704 | |||
2705 | switch (skb->data[0]) { | ||
2706 | case CCP_CONFREQ: | ||
2707 | if (is->debug & 0x10) | ||
2708 | printk(KERN_DEBUG "Disable compression here!\n"); | ||
2709 | if (proto == PPP_CCP) | ||
2710 | mis->compflags &= ~SC_COMP_ON; | ||
2711 | else | ||
2712 | is->compflags &= ~SC_LINK_COMP_ON; | ||
2713 | break; | ||
2714 | case CCP_TERMREQ: | ||
2715 | case CCP_TERMACK: | ||
2716 | if (is->debug & 0x10) | ||
2717 | printk(KERN_DEBUG "Disable (de)compression here!\n"); | ||
2718 | if (proto == PPP_CCP) | ||
2719 | mis->compflags &= ~(SC_DECOMP_ON | SC_COMP_ON); | ||
2720 | else | ||
2721 | is->compflags &= ~(SC_LINK_DECOMP_ON | SC_LINK_COMP_ON); | ||
2722 | break; | ||
2723 | case CCP_CONFACK: | ||
2724 | /* if we RECEIVE an ackowledge we enable the decompressor */ | ||
2725 | if (is->debug & 0x10) | ||
2726 | printk(KERN_DEBUG "Enable decompression here!\n"); | ||
2727 | if (proto == PPP_CCP) { | ||
2728 | if (!mis->decompressor) | ||
2729 | break; | ||
2730 | mis->compflags |= SC_DECOMP_ON; | ||
2731 | } else { | ||
2732 | if (!is->decompressor) | ||
2733 | break; | ||
2734 | is->compflags |= SC_LINK_DECOMP_ON; | ||
2735 | } | ||
2736 | break; | ||
2737 | |||
2738 | case CCP_RESETACK: | ||
2739 | printk(KERN_DEBUG "Received ResetAck from peer\n"); | ||
2740 | len = (skb->data[2] << 8) | skb->data[3]; | ||
2741 | len -= 4; | ||
2742 | |||
2743 | if (proto == PPP_CCP) { | ||
2744 | /* If a reset Ack was outstanding for this id, then | ||
2745 | clean up the state engine */ | ||
2746 | isdn_ppp_ccp_reset_ack_rcvd(mis, skb->data[1]); | ||
2747 | if (mis->decompressor && mis->decomp_stat) | ||
2748 | mis->decompressor-> | ||
2749 | reset(mis->decomp_stat, | ||
2750 | skb->data[0], | ||
2751 | skb->data[1], | ||
2752 | len ? &skb->data[4] : NULL, | ||
2753 | len, NULL); | ||
2754 | /* TODO: This is not easy to decide here */ | ||
2755 | mis->compflags &= ~SC_DECOMP_DISCARD; | ||
2756 | } | ||
2757 | else { | ||
2758 | isdn_ppp_ccp_reset_ack_rcvd(is, skb->data[1]); | ||
2759 | if (is->link_decompressor && is->link_decomp_stat) | ||
2760 | is->link_decompressor-> | ||
2761 | reset(is->link_decomp_stat, | ||
2762 | skb->data[0], | ||
2763 | skb->data[1], | ||
2764 | len ? &skb->data[4] : NULL, | ||
2765 | len, NULL); | ||
2766 | /* TODO: neither here */ | ||
2767 | is->compflags &= ~SC_LINK_DECOMP_DISCARD; | ||
2768 | } | ||
2769 | break; | ||
2770 | |||
2771 | case CCP_RESETREQ: | ||
2772 | printk(KERN_DEBUG "Received ResetReq from peer\n"); | ||
2773 | /* Receiving a ResetReq means we must reset our compressor */ | ||
2774 | /* Set up reset params for the reset entry */ | ||
2775 | memset(&rsparm, 0, sizeof(rsparm)); | ||
2776 | rsparm.data = rsdata; | ||
2777 | rsparm.maxdlen = IPPP_RESET_MAXDATABYTES; | ||
2778 | /* Isolate data length */ | ||
2779 | len = (skb->data[2] << 8) | skb->data[3]; | ||
2780 | len -= 4; | ||
2781 | if (proto == PPP_CCP) { | ||
2782 | if (mis->compressor && mis->comp_stat) | ||
2783 | mis->compressor-> | ||
2784 | reset(mis->comp_stat, | ||
2785 | skb->data[0], | ||
2786 | skb->data[1], | ||
2787 | len ? &skb->data[4] : NULL, | ||
2788 | len, &rsparm); | ||
2789 | } | ||
2790 | else { | ||
2791 | if (is->link_compressor && is->link_comp_stat) | ||
2792 | is->link_compressor-> | ||
2793 | reset(is->link_comp_stat, | ||
2794 | skb->data[0], | ||
2795 | skb->data[1], | ||
2796 | len ? &skb->data[4] : NULL, | ||
2797 | len, &rsparm); | ||
2798 | } | ||
2799 | /* Ack the Req as specified by rsparm */ | ||
2800 | if (rsparm.valid) { | ||
2801 | /* Compressor reset handler decided how to answer */ | ||
2802 | if (rsparm.rsend) { | ||
2803 | /* We should send a Frame */ | ||
2804 | isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK, | ||
2805 | rsparm.idval ? rsparm.id | ||
2806 | : skb->data[1], | ||
2807 | rsparm.dtval ? | ||
2808 | rsparm.data : NULL, | ||
2809 | rsparm.dtval ? | ||
2810 | rsparm.dlen : 0); | ||
2811 | } else { | ||
2812 | printk(KERN_DEBUG "ResetAck suppressed\n"); | ||
2813 | } | ||
2814 | } else { | ||
2815 | /* We answer with a straight reflected Ack */ | ||
2816 | isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK, | ||
2817 | skb->data[1], | ||
2818 | len ? &skb->data[4] : NULL, | ||
2819 | len); | ||
2820 | } | ||
2821 | break; | ||
2822 | } | ||
2823 | } | ||
2824 | |||
2825 | |||
2826 | /* | ||
2827 | * Daemon sends a CCP frame ... | ||
2828 | */ | ||
2829 | |||
2830 | /* TODO: Clean this up with new Reset semantics */ | ||
2831 | |||
2832 | /* I believe the CCP handling as-is is done wrong. Compressed frames | ||
2833 | * should only be sent/received after CCP reaches UP state, which means | ||
2834 | * both sides have sent CONF_ACK. Currently, we handle both directions | ||
2835 | * independently, which means we may accept compressed frames too early | ||
2836 | * (supposedly not a problem), but may also mean we send compressed frames | ||
2837 | * too early, which may turn out to be a problem. | ||
2838 | * This part of state machine should actually be handled by (i)pppd, but | ||
2839 | * that's too big of a change now. --kai | ||
2840 | */ | ||
2841 | |||
2842 | /* Actually, we might turn this into an advantage: deal with the RFC in | ||
2843 | * the old tradition of beeing generous on what we accept, but beeing | ||
2844 | * strict on what we send. Thus we should just | ||
2845 | * - accept compressed frames as soon as decompression is negotiated | ||
2846 | * - send compressed frames only when decomp *and* comp are negotiated | ||
2847 | * - drop rx compressed frames if we cannot decomp (instead of pushing them | ||
2848 | * up to ipppd) | ||
2849 | * and I tried to modify this file according to that. --abp | ||
2850 | */ | ||
2851 | |||
2852 | static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb) | ||
2853 | { | ||
2854 | struct ippp_struct *mis, *is; | ||
2855 | int proto, slot = lp->ppp_slot; | ||
2856 | unsigned char *data; | ||
2857 | |||
2858 | if (!skb || skb->len < 3) | ||
2859 | return; | ||
2860 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
2861 | printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n", | ||
2862 | __func__, slot); | ||
2863 | return; | ||
2864 | } | ||
2865 | is = ippp_table[slot]; | ||
2866 | /* Daemon may send with or without address and control field comp */ | ||
2867 | data = skb->data; | ||
2868 | if (!(is->pppcfg & SC_COMP_AC) && data[0] == 0xff && data[1] == 0x03) { | ||
2869 | data += 2; | ||
2870 | if (skb->len < 5) | ||
2871 | return; | ||
2872 | } | ||
2873 | |||
2874 | proto = ((int)data[0]<<8) + data[1]; | ||
2875 | if (proto != PPP_CCP && proto != PPP_CCPFRAG) | ||
2876 | return; | ||
2877 | |||
2878 | printk(KERN_DEBUG "Received CCP frame from daemon:\n"); | ||
2879 | isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit, lp->ppp_slot); | ||
2880 | |||
2881 | if (lp->master) { | ||
2882 | slot = ISDN_MASTER_PRIV(lp)->ppp_slot; | ||
2883 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
2884 | printk(KERN_ERR "%s: slot(%d) out of range\n", | ||
2885 | __func__, slot); | ||
2886 | return; | ||
2887 | } | ||
2888 | mis = ippp_table[slot]; | ||
2889 | } else | ||
2890 | mis = is; | ||
2891 | if (mis != is) | ||
2892 | printk(KERN_DEBUG "isdn_ppp: Ouch! Master CCP sends on slave slot!\n"); | ||
2893 | |||
2894 | switch (data[2]) { | ||
2895 | case CCP_CONFREQ: | ||
2896 | if (is->debug & 0x10) | ||
2897 | printk(KERN_DEBUG "Disable decompression here!\n"); | ||
2898 | if (proto == PPP_CCP) | ||
2899 | is->compflags &= ~SC_DECOMP_ON; | ||
2900 | else | ||
2901 | is->compflags &= ~SC_LINK_DECOMP_ON; | ||
2902 | break; | ||
2903 | case CCP_TERMREQ: | ||
2904 | case CCP_TERMACK: | ||
2905 | if (is->debug & 0x10) | ||
2906 | printk(KERN_DEBUG "Disable (de)compression here!\n"); | ||
2907 | if (proto == PPP_CCP) | ||
2908 | is->compflags &= ~(SC_DECOMP_ON | SC_COMP_ON); | ||
2909 | else | ||
2910 | is->compflags &= ~(SC_LINK_DECOMP_ON | SC_LINK_COMP_ON); | ||
2911 | break; | ||
2912 | case CCP_CONFACK: | ||
2913 | /* if we SEND an ackowledge we can/must enable the compressor */ | ||
2914 | if (is->debug & 0x10) | ||
2915 | printk(KERN_DEBUG "Enable compression here!\n"); | ||
2916 | if (proto == PPP_CCP) { | ||
2917 | if (!is->compressor) | ||
2918 | break; | ||
2919 | is->compflags |= SC_COMP_ON; | ||
2920 | } else { | ||
2921 | if (!is->compressor) | ||
2922 | break; | ||
2923 | is->compflags |= SC_LINK_COMP_ON; | ||
2924 | } | ||
2925 | break; | ||
2926 | case CCP_RESETACK: | ||
2927 | /* If we send a ACK we should reset our compressor */ | ||
2928 | if (is->debug & 0x10) | ||
2929 | printk(KERN_DEBUG "Reset decompression state here!\n"); | ||
2930 | printk(KERN_DEBUG "ResetAck from daemon passed by\n"); | ||
2931 | if (proto == PPP_CCP) { | ||
2932 | /* link to master? */ | ||
2933 | if (is->compressor && is->comp_stat) | ||
2934 | is->compressor->reset(is->comp_stat, 0, 0, | ||
2935 | NULL, 0, NULL); | ||
2936 | is->compflags &= ~SC_COMP_DISCARD; | ||
2937 | } | ||
2938 | else { | ||
2939 | if (is->link_compressor && is->link_comp_stat) | ||
2940 | is->link_compressor->reset(is->link_comp_stat, | ||
2941 | 0, 0, NULL, 0, NULL); | ||
2942 | is->compflags &= ~SC_LINK_COMP_DISCARD; | ||
2943 | } | ||
2944 | break; | ||
2945 | case CCP_RESETREQ: | ||
2946 | /* Just let it pass by */ | ||
2947 | printk(KERN_DEBUG "ResetReq from daemon passed by\n"); | ||
2948 | break; | ||
2949 | } | ||
2950 | } | ||
2951 | |||
2952 | int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc) | ||
2953 | { | ||
2954 | ipc->next = ipc_head; | ||
2955 | ipc->prev = NULL; | ||
2956 | if (ipc_head) { | ||
2957 | ipc_head->prev = ipc; | ||
2958 | } | ||
2959 | ipc_head = ipc; | ||
2960 | return 0; | ||
2961 | } | ||
2962 | |||
2963 | int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *ipc) | ||
2964 | { | ||
2965 | if (ipc->prev) | ||
2966 | ipc->prev->next = ipc->next; | ||
2967 | else | ||
2968 | ipc_head = ipc->next; | ||
2969 | if (ipc->next) | ||
2970 | ipc->next->prev = ipc->prev; | ||
2971 | ipc->prev = ipc->next = NULL; | ||
2972 | return 0; | ||
2973 | } | ||
2974 | |||
2975 | static int isdn_ppp_set_compressor(struct ippp_struct *is, struct isdn_ppp_comp_data *data) | ||
2976 | { | ||
2977 | struct isdn_ppp_compressor *ipc = ipc_head; | ||
2978 | int ret; | ||
2979 | void *stat; | ||
2980 | int num = data->num; | ||
2981 | |||
2982 | if (is->debug & 0x10) | ||
2983 | printk(KERN_DEBUG "[%d] Set %s type %d\n", is->unit, | ||
2984 | (data->flags & IPPP_COMP_FLAG_XMIT) ? "compressor" : "decompressor", num); | ||
2985 | |||
2986 | /* If is has no valid reset state vector, we cannot allocate a | ||
2987 | decompressor. The decompressor would cause reset transactions | ||
2988 | sooner or later, and they need that vector. */ | ||
2989 | |||
2990 | if (!(data->flags & IPPP_COMP_FLAG_XMIT) && !is->reset) { | ||
2991 | printk(KERN_ERR "ippp_ccp: no reset data structure - can't" | ||
2992 | " allow decompression.\n"); | ||
2993 | return -ENOMEM; | ||
2994 | } | ||
2995 | |||
2996 | while (ipc) { | ||
2997 | if (ipc->num == num) { | ||
2998 | stat = ipc->alloc(data); | ||
2999 | if (stat) { | ||
3000 | ret = ipc->init(stat, data, is->unit, 0); | ||
3001 | if (!ret) { | ||
3002 | printk(KERN_ERR "Can't init (de)compression!\n"); | ||
3003 | ipc->free(stat); | ||
3004 | stat = NULL; | ||
3005 | break; | ||
3006 | } | ||
3007 | } | ||
3008 | else { | ||
3009 | printk(KERN_ERR "Can't alloc (de)compression!\n"); | ||
3010 | break; | ||
3011 | } | ||
3012 | |||
3013 | if (data->flags & IPPP_COMP_FLAG_XMIT) { | ||
3014 | if (data->flags & IPPP_COMP_FLAG_LINK) { | ||
3015 | if (is->link_comp_stat) | ||
3016 | is->link_compressor->free(is->link_comp_stat); | ||
3017 | is->link_comp_stat = stat; | ||
3018 | is->link_compressor = ipc; | ||
3019 | } | ||
3020 | else { | ||
3021 | if (is->comp_stat) | ||
3022 | is->compressor->free(is->comp_stat); | ||
3023 | is->comp_stat = stat; | ||
3024 | is->compressor = ipc; | ||
3025 | } | ||
3026 | } | ||
3027 | else { | ||
3028 | if (data->flags & IPPP_COMP_FLAG_LINK) { | ||
3029 | if (is->link_decomp_stat) | ||
3030 | is->link_decompressor->free(is->link_decomp_stat); | ||
3031 | is->link_decomp_stat = stat; | ||
3032 | is->link_decompressor = ipc; | ||
3033 | } | ||
3034 | else { | ||
3035 | if (is->decomp_stat) | ||
3036 | is->decompressor->free(is->decomp_stat); | ||
3037 | is->decomp_stat = stat; | ||
3038 | is->decompressor = ipc; | ||
3039 | } | ||
3040 | } | ||
3041 | return 0; | ||
3042 | } | ||
3043 | ipc = ipc->next; | ||
3044 | } | ||
3045 | return -EINVAL; | ||
3046 | } | ||
diff --git a/drivers/isdn/i4l/isdn_ppp.h b/drivers/isdn/i4l/isdn_ppp.h deleted file mode 100644 index 34b8a2ce84f3..000000000000 --- a/drivers/isdn/i4l/isdn_ppp.h +++ /dev/null | |||
@@ -1,41 +0,0 @@ | |||
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 __poll_t 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 | ||
diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c deleted file mode 100644 index 43700fc19a31..000000000000 --- a/drivers/isdn/i4l/isdn_tty.c +++ /dev/null | |||
@@ -1,3756 +0,0 @@ | |||
1 | /* | ||
2 | * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel). | ||
3 | * | ||
4 | * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de) | ||
5 | * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg | ||
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 | #undef ISDN_TTY_STAT_DEBUG | ||
12 | |||
13 | #include <linux/isdn.h> | ||
14 | #include <linux/serial.h> /* ASYNC_* flags */ | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/mutex.h> | ||
18 | #include <linux/sched/signal.h> | ||
19 | #include "isdn_common.h" | ||
20 | #include "isdn_tty.h" | ||
21 | #ifdef CONFIG_ISDN_AUDIO | ||
22 | #include "isdn_audio.h" | ||
23 | #define VBUF 0x3e0 | ||
24 | #define VBUFX (VBUF/16) | ||
25 | #endif | ||
26 | |||
27 | #define FIX_FILE_TRANSFER | ||
28 | #define DUMMY_HAYES_AT | ||
29 | |||
30 | /* Prototypes */ | ||
31 | |||
32 | static DEFINE_MUTEX(modem_info_mutex); | ||
33 | static int isdn_tty_edit_at(const char *, int, modem_info *); | ||
34 | static void isdn_tty_check_esc(const u_char *, u_char, int, int *, u_long *); | ||
35 | static void isdn_tty_modem_reset_regs(modem_info *, int); | ||
36 | static void isdn_tty_cmd_ATA(modem_info *); | ||
37 | static void isdn_tty_flush_buffer(struct tty_struct *); | ||
38 | static void isdn_tty_modem_result(int, modem_info *); | ||
39 | #ifdef CONFIG_ISDN_AUDIO | ||
40 | static int isdn_tty_countDLE(unsigned char *, int); | ||
41 | #endif | ||
42 | |||
43 | /* Leave this unchanged unless you know what you do! */ | ||
44 | #define MODEM_PARANOIA_CHECK | ||
45 | #define MODEM_DO_RESTART | ||
46 | |||
47 | static int bit2si[8] = | ||
48 | {1, 5, 7, 7, 7, 7, 7, 7}; | ||
49 | static int si2bit[8] = | ||
50 | {4, 1, 4, 4, 4, 4, 4, 4}; | ||
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 | struct tty_port *port = &info->port; | ||
65 | int c; | ||
66 | int len; | ||
67 | char last; | ||
68 | |||
69 | if (!info->online) | ||
70 | return 0; | ||
71 | |||
72 | if (!(info->mcr & UART_MCR_RTS)) | ||
73 | return 0; | ||
74 | |||
75 | len = skb->len | ||
76 | #ifdef CONFIG_ISDN_AUDIO | ||
77 | + ISDN_AUDIO_SKB_DLECOUNT(skb) | ||
78 | #endif | ||
79 | ; | ||
80 | |||
81 | c = tty_buffer_request_room(port, len); | ||
82 | if (c < len) | ||
83 | return 0; | ||
84 | |||
85 | #ifdef CONFIG_ISDN_AUDIO | ||
86 | if (ISDN_AUDIO_SKB_DLECOUNT(skb)) { | ||
87 | int l = skb->len; | ||
88 | unsigned char *dp = skb->data; | ||
89 | while (--l) { | ||
90 | if (*dp == DLE) | ||
91 | tty_insert_flip_char(port, DLE, 0); | ||
92 | tty_insert_flip_char(port, *dp++, 0); | ||
93 | } | ||
94 | if (*dp == DLE) | ||
95 | tty_insert_flip_char(port, DLE, 0); | ||
96 | last = *dp; | ||
97 | } else { | ||
98 | #endif | ||
99 | if (len > 1) | ||
100 | tty_insert_flip_string(port, skb->data, len - 1); | ||
101 | last = skb->data[len - 1]; | ||
102 | #ifdef CONFIG_ISDN_AUDIO | ||
103 | } | ||
104 | #endif | ||
105 | if (info->emu.mdmreg[REG_CPPP] & BIT_CPPP) | ||
106 | tty_insert_flip_char(port, last, 0xFF); | ||
107 | else | ||
108 | tty_insert_flip_char(port, last, TTY_NORMAL); | ||
109 | tty_flip_buffer_push(port); | ||
110 | kfree_skb(skb); | ||
111 | |||
112 | return 1; | ||
113 | } | ||
114 | |||
115 | /* isdn_tty_readmodem() is called periodically from within timer-interrupt. | ||
116 | * It tries getting received data from the receive queue an stuff it into | ||
117 | * the tty's flip-buffer. | ||
118 | */ | ||
119 | void | ||
120 | isdn_tty_readmodem(void) | ||
121 | { | ||
122 | int resched = 0; | ||
123 | int midx; | ||
124 | int i; | ||
125 | int r; | ||
126 | modem_info *info; | ||
127 | |||
128 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
129 | midx = dev->m_idx[i]; | ||
130 | if (midx < 0) | ||
131 | continue; | ||
132 | |||
133 | info = &dev->mdm.info[midx]; | ||
134 | if (!info->online) | ||
135 | continue; | ||
136 | |||
137 | r = 0; | ||
138 | #ifdef CONFIG_ISDN_AUDIO | ||
139 | isdn_audio_eval_dtmf(info); | ||
140 | if ((info->vonline & 1) && (info->emu.vpar[1])) | ||
141 | isdn_audio_eval_silence(info); | ||
142 | #endif | ||
143 | if (info->mcr & UART_MCR_RTS) { | ||
144 | /* CISCO AsyncPPP Hack */ | ||
145 | if (!(info->emu.mdmreg[REG_CPPP] & BIT_CPPP)) | ||
146 | r = isdn_readbchan_tty(info->isdn_driver, | ||
147 | info->isdn_channel, | ||
148 | &info->port, 0); | ||
149 | else | ||
150 | r = isdn_readbchan_tty(info->isdn_driver, | ||
151 | info->isdn_channel, | ||
152 | &info->port, 1); | ||
153 | if (r) | ||
154 | tty_flip_buffer_push(&info->port); | ||
155 | } else | ||
156 | r = 1; | ||
157 | |||
158 | if (r) { | ||
159 | info->rcvsched = 0; | ||
160 | resched = 1; | ||
161 | } else | ||
162 | info->rcvsched = 1; | ||
163 | } | ||
164 | if (!resched) | ||
165 | isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 0); | ||
166 | } | ||
167 | |||
168 | int | ||
169 | isdn_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb) | ||
170 | { | ||
171 | ulong flags; | ||
172 | int midx; | ||
173 | #ifdef CONFIG_ISDN_AUDIO | ||
174 | int ifmt; | ||
175 | #endif | ||
176 | modem_info *info; | ||
177 | |||
178 | if ((midx = dev->m_idx[i]) < 0) { | ||
179 | /* if midx is invalid, packet is not for tty */ | ||
180 | return 0; | ||
181 | } | ||
182 | info = &dev->mdm.info[midx]; | ||
183 | #ifdef CONFIG_ISDN_AUDIO | ||
184 | ifmt = 1; | ||
185 | |||
186 | if ((info->vonline) && (!info->emu.vpar[4])) | ||
187 | isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt); | ||
188 | if ((info->vonline & 1) && (info->emu.vpar[1])) | ||
189 | isdn_audio_calc_silence(info, skb->data, skb->len, ifmt); | ||
190 | #endif | ||
191 | if ((info->online < 2) | ||
192 | #ifdef CONFIG_ISDN_AUDIO | ||
193 | && (!(info->vonline & 1)) | ||
194 | #endif | ||
195 | ) { | ||
196 | /* If Modem not listening, drop data */ | ||
197 | kfree_skb(skb); | ||
198 | return 1; | ||
199 | } | ||
200 | if (info->emu.mdmreg[REG_T70] & BIT_T70) { | ||
201 | if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT) { | ||
202 | /* T.70 decoding: throw away the T.70 header (2 or 4 bytes) */ | ||
203 | if (skb->data[0] == 3) /* pure data packet -> 4 byte headers */ | ||
204 | skb_pull(skb, 4); | ||
205 | else | ||
206 | if (skb->data[0] == 1) /* keepalive packet -> 2 byte hdr */ | ||
207 | skb_pull(skb, 2); | ||
208 | } else | ||
209 | /* T.70 decoding: Simply throw away the T.70 header (4 bytes) */ | ||
210 | if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1))) | ||
211 | skb_pull(skb, 4); | ||
212 | } | ||
213 | #ifdef CONFIG_ISDN_AUDIO | ||
214 | ISDN_AUDIO_SKB_DLECOUNT(skb) = 0; | ||
215 | ISDN_AUDIO_SKB_LOCK(skb) = 0; | ||
216 | if (info->vonline & 1) { | ||
217 | /* voice conversion/compression */ | ||
218 | switch (info->emu.vpar[3]) { | ||
219 | case 2: | ||
220 | case 3: | ||
221 | case 4: | ||
222 | /* adpcm | ||
223 | * Since compressed data takes less | ||
224 | * space, we can overwrite the buffer. | ||
225 | */ | ||
226 | skb_trim(skb, isdn_audio_xlaw2adpcm(info->adpcmr, | ||
227 | ifmt, | ||
228 | skb->data, | ||
229 | skb->data, | ||
230 | skb->len)); | ||
231 | break; | ||
232 | case 5: | ||
233 | /* a-law */ | ||
234 | if (!ifmt) | ||
235 | isdn_audio_ulaw2alaw(skb->data, skb->len); | ||
236 | break; | ||
237 | case 6: | ||
238 | /* u-law */ | ||
239 | if (ifmt) | ||
240 | isdn_audio_alaw2ulaw(skb->data, skb->len); | ||
241 | break; | ||
242 | } | ||
243 | ISDN_AUDIO_SKB_DLECOUNT(skb) = | ||
244 | isdn_tty_countDLE(skb->data, skb->len); | ||
245 | } | ||
246 | #ifdef CONFIG_ISDN_TTY_FAX | ||
247 | else { | ||
248 | if (info->faxonline & 2) { | ||
249 | isdn_tty_fax_bitorder(info, skb); | ||
250 | ISDN_AUDIO_SKB_DLECOUNT(skb) = | ||
251 | isdn_tty_countDLE(skb->data, skb->len); | ||
252 | } | ||
253 | } | ||
254 | #endif | ||
255 | #endif | ||
256 | /* Try to deliver directly via tty-buf if queue is empty */ | ||
257 | spin_lock_irqsave(&info->readlock, flags); | ||
258 | if (skb_queue_empty(&dev->drv[di]->rpqueue[channel])) | ||
259 | if (isdn_tty_try_read(info, skb)) { | ||
260 | spin_unlock_irqrestore(&info->readlock, flags); | ||
261 | return 1; | ||
262 | } | ||
263 | /* Direct deliver failed or queue wasn't empty. | ||
264 | * Queue up for later dequeueing via timer-irq. | ||
265 | */ | ||
266 | __skb_queue_tail(&dev->drv[di]->rpqueue[channel], skb); | ||
267 | dev->drv[di]->rcvcount[channel] += | ||
268 | (skb->len | ||
269 | #ifdef CONFIG_ISDN_AUDIO | ||
270 | + ISDN_AUDIO_SKB_DLECOUNT(skb) | ||
271 | #endif | ||
272 | ); | ||
273 | spin_unlock_irqrestore(&info->readlock, flags); | ||
274 | /* Schedule dequeuing */ | ||
275 | if ((dev->modempoll) && (info->rcvsched)) | ||
276 | isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); | ||
277 | return 1; | ||
278 | } | ||
279 | |||
280 | static void | ||
281 | isdn_tty_cleanup_xmit(modem_info *info) | ||
282 | { | ||
283 | skb_queue_purge(&info->xmit_queue); | ||
284 | #ifdef CONFIG_ISDN_AUDIO | ||
285 | skb_queue_purge(&info->dtmf_queue); | ||
286 | #endif | ||
287 | } | ||
288 | |||
289 | static void | ||
290 | isdn_tty_tint(modem_info *info) | ||
291 | { | ||
292 | struct sk_buff *skb = skb_dequeue(&info->xmit_queue); | ||
293 | int len, slen; | ||
294 | |||
295 | if (!skb) | ||
296 | return; | ||
297 | len = skb->len; | ||
298 | if ((slen = isdn_writebuf_skb_stub(info->isdn_driver, | ||
299 | info->isdn_channel, 1, skb)) == len) { | ||
300 | struct tty_struct *tty = info->port.tty; | ||
301 | info->send_outstanding++; | ||
302 | info->msr &= ~UART_MSR_CTS; | ||
303 | info->lsr &= ~UART_LSR_TEMT; | ||
304 | tty_wakeup(tty); | ||
305 | return; | ||
306 | } | ||
307 | if (slen < 0) { | ||
308 | /* Error: no channel, already shutdown, or wrong parameter */ | ||
309 | dev_kfree_skb(skb); | ||
310 | return; | ||
311 | } | ||
312 | skb_queue_head(&info->xmit_queue, skb); | ||
313 | } | ||
314 | |||
315 | #ifdef CONFIG_ISDN_AUDIO | ||
316 | static int | ||
317 | isdn_tty_countDLE(unsigned char *buf, int len) | ||
318 | { | ||
319 | int count = 0; | ||
320 | |||
321 | while (len--) | ||
322 | if (*buf++ == DLE) | ||
323 | count++; | ||
324 | return count; | ||
325 | } | ||
326 | |||
327 | /* This routine is called from within isdn_tty_write() to perform | ||
328 | * DLE-decoding when sending audio-data. | ||
329 | */ | ||
330 | static int | ||
331 | isdn_tty_handleDLEdown(modem_info *info, atemu *m, int len) | ||
332 | { | ||
333 | unsigned char *p = &info->port.xmit_buf[info->xmit_count]; | ||
334 | int count = 0; | ||
335 | |||
336 | while (len > 0) { | ||
337 | if (m->lastDLE) { | ||
338 | m->lastDLE = 0; | ||
339 | switch (*p) { | ||
340 | case DLE: | ||
341 | /* Escape code */ | ||
342 | if (len > 1) | ||
343 | memmove(p, p + 1, len - 1); | ||
344 | p--; | ||
345 | count++; | ||
346 | break; | ||
347 | case ETX: | ||
348 | /* End of data */ | ||
349 | info->vonline |= 4; | ||
350 | return count; | ||
351 | case DC4: | ||
352 | /* Abort RX */ | ||
353 | info->vonline &= ~1; | ||
354 | #ifdef ISDN_DEBUG_MODEM_VOICE | ||
355 | printk(KERN_DEBUG | ||
356 | "DLEdown: got DLE-DC4, send DLE-ETX on ttyI%d\n", | ||
357 | info->line); | ||
358 | #endif | ||
359 | isdn_tty_at_cout("\020\003", info); | ||
360 | if (!info->vonline) { | ||
361 | #ifdef ISDN_DEBUG_MODEM_VOICE | ||
362 | printk(KERN_DEBUG | ||
363 | "DLEdown: send VCON on ttyI%d\n", | ||
364 | info->line); | ||
365 | #endif | ||
366 | isdn_tty_at_cout("\r\nVCON\r\n", info); | ||
367 | } | ||
368 | /* Fall through */ | ||
369 | case 'q': | ||
370 | case 's': | ||
371 | /* Silence */ | ||
372 | if (len > 1) | ||
373 | memmove(p, p + 1, len - 1); | ||
374 | p--; | ||
375 | break; | ||
376 | } | ||
377 | } else { | ||
378 | if (*p == DLE) | ||
379 | m->lastDLE = 1; | ||
380 | else | ||
381 | count++; | ||
382 | } | ||
383 | p++; | ||
384 | len--; | ||
385 | } | ||
386 | if (len < 0) { | ||
387 | printk(KERN_WARNING "isdn_tty: len<0 in DLEdown\n"); | ||
388 | return 0; | ||
389 | } | ||
390 | return count; | ||
391 | } | ||
392 | |||
393 | /* This routine is called from within isdn_tty_write() when receiving | ||
394 | * audio-data. It interrupts receiving, if an character other than | ||
395 | * ^S or ^Q is sent. | ||
396 | */ | ||
397 | static int | ||
398 | isdn_tty_end_vrx(const char *buf, int c) | ||
399 | { | ||
400 | char ch; | ||
401 | |||
402 | while (c--) { | ||
403 | ch = *buf; | ||
404 | if ((ch != 0x11) && (ch != 0x13)) | ||
405 | return 1; | ||
406 | buf++; | ||
407 | } | ||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | static int voice_cf[7] = | ||
412 | {0, 0, 4, 3, 2, 0, 0}; | ||
413 | |||
414 | #endif /* CONFIG_ISDN_AUDIO */ | ||
415 | |||
416 | /* isdn_tty_senddown() is called either directly from within isdn_tty_write() | ||
417 | * or via timer-interrupt from within isdn_tty_modem_xmit(). It pulls | ||
418 | * outgoing data from the tty's xmit-buffer, handles voice-decompression or | ||
419 | * T.70 if necessary, and finally queues it up for sending via isdn_tty_tint. | ||
420 | */ | ||
421 | static void | ||
422 | isdn_tty_senddown(modem_info *info) | ||
423 | { | ||
424 | int buflen; | ||
425 | int skb_res; | ||
426 | #ifdef CONFIG_ISDN_AUDIO | ||
427 | int audio_len; | ||
428 | #endif | ||
429 | struct sk_buff *skb; | ||
430 | |||
431 | #ifdef CONFIG_ISDN_AUDIO | ||
432 | if (info->vonline & 4) { | ||
433 | info->vonline &= ~6; | ||
434 | if (!info->vonline) { | ||
435 | #ifdef ISDN_DEBUG_MODEM_VOICE | ||
436 | printk(KERN_DEBUG | ||
437 | "senddown: send VCON on ttyI%d\n", | ||
438 | info->line); | ||
439 | #endif | ||
440 | isdn_tty_at_cout("\r\nVCON\r\n", info); | ||
441 | } | ||
442 | } | ||
443 | #endif | ||
444 | if (!(buflen = info->xmit_count)) | ||
445 | return; | ||
446 | if ((info->emu.mdmreg[REG_CTS] & BIT_CTS) != 0) | ||
447 | info->msr &= ~UART_MSR_CTS; | ||
448 | info->lsr &= ~UART_LSR_TEMT; | ||
449 | /* info->xmit_count is modified here and in isdn_tty_write(). | ||
450 | * So we return here if isdn_tty_write() is in the | ||
451 | * critical section. | ||
452 | */ | ||
453 | atomic_inc(&info->xmit_lock); | ||
454 | if (!(atomic_dec_and_test(&info->xmit_lock))) | ||
455 | return; | ||
456 | if (info->isdn_driver < 0) { | ||
457 | info->xmit_count = 0; | ||
458 | return; | ||
459 | } | ||
460 | skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4; | ||
461 | #ifdef CONFIG_ISDN_AUDIO | ||
462 | if (info->vonline & 2) | ||
463 | audio_len = buflen * voice_cf[info->emu.vpar[3]]; | ||
464 | else | ||
465 | audio_len = 0; | ||
466 | skb = dev_alloc_skb(skb_res + buflen + audio_len); | ||
467 | #else | ||
468 | skb = dev_alloc_skb(skb_res + buflen); | ||
469 | #endif | ||
470 | if (!skb) { | ||
471 | printk(KERN_WARNING | ||
472 | "isdn_tty: Out of memory in ttyI%d senddown\n", | ||
473 | info->line); | ||
474 | return; | ||
475 | } | ||
476 | skb_reserve(skb, skb_res); | ||
477 | skb_put_data(skb, info->port.xmit_buf, buflen); | ||
478 | info->xmit_count = 0; | ||
479 | #ifdef CONFIG_ISDN_AUDIO | ||
480 | if (info->vonline & 2) { | ||
481 | /* For now, ifmt is fixed to 1 (alaw), since this | ||
482 | * is used with ISDN everywhere in the world, except | ||
483 | * US, Canada and Japan. | ||
484 | * Later, when US-ISDN protocols are implemented, | ||
485 | * this setting will depend on the D-channel protocol. | ||
486 | */ | ||
487 | int ifmt = 1; | ||
488 | |||
489 | /* voice conversion/decompression */ | ||
490 | switch (info->emu.vpar[3]) { | ||
491 | case 2: | ||
492 | case 3: | ||
493 | case 4: | ||
494 | /* adpcm, compatible to ZyXel 1496 modem | ||
495 | * with ROM revision 6.01 | ||
496 | */ | ||
497 | audio_len = isdn_audio_adpcm2xlaw(info->adpcms, | ||
498 | ifmt, | ||
499 | skb->data, | ||
500 | skb_put(skb, audio_len), | ||
501 | buflen); | ||
502 | skb_pull(skb, buflen); | ||
503 | skb_trim(skb, audio_len); | ||
504 | break; | ||
505 | case 5: | ||
506 | /* a-law */ | ||
507 | if (!ifmt) | ||
508 | isdn_audio_alaw2ulaw(skb->data, | ||
509 | buflen); | ||
510 | break; | ||
511 | case 6: | ||
512 | /* u-law */ | ||
513 | if (ifmt) | ||
514 | isdn_audio_ulaw2alaw(skb->data, | ||
515 | buflen); | ||
516 | break; | ||
517 | } | ||
518 | } | ||
519 | #endif /* CONFIG_ISDN_AUDIO */ | ||
520 | if (info->emu.mdmreg[REG_T70] & BIT_T70) { | ||
521 | /* Add T.70 simplified header */ | ||
522 | if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT) | ||
523 | memcpy(skb_push(skb, 2), "\1\0", 2); | ||
524 | else | ||
525 | memcpy(skb_push(skb, 4), "\1\0\1\0", 4); | ||
526 | } | ||
527 | skb_queue_tail(&info->xmit_queue, skb); | ||
528 | } | ||
529 | |||
530 | /************************************************************ | ||
531 | * | ||
532 | * Modem-functions | ||
533 | * | ||
534 | * mostly "stolen" from original Linux-serial.c and friends. | ||
535 | * | ||
536 | ************************************************************/ | ||
537 | |||
538 | /* The next routine is called once from within timer-interrupt | ||
539 | * triggered within isdn_tty_modem_ncarrier(). It calls | ||
540 | * isdn_tty_modem_result() to stuff a "NO CARRIER" Message | ||
541 | * into the tty's buffer. | ||
542 | */ | ||
543 | static void | ||
544 | isdn_tty_modem_do_ncarrier(struct timer_list *t) | ||
545 | { | ||
546 | modem_info *info = from_timer(info, t, nc_timer); | ||
547 | isdn_tty_modem_result(RESULT_NO_CARRIER, info); | ||
548 | } | ||
549 | |||
550 | /* Next routine is called, whenever the DTR-signal is raised. | ||
551 | * It checks the ncarrier-flag, and triggers the above routine | ||
552 | * when necessary. The ncarrier-flag is set, whenever DTR goes | ||
553 | * low. | ||
554 | */ | ||
555 | static void | ||
556 | isdn_tty_modem_ncarrier(modem_info *info) | ||
557 | { | ||
558 | if (info->ncarrier) { | ||
559 | info->nc_timer.expires = jiffies + HZ; | ||
560 | add_timer(&info->nc_timer); | ||
561 | } | ||
562 | } | ||
563 | |||
564 | /* | ||
565 | * return the usage calculated by si and layer 2 protocol | ||
566 | */ | ||
567 | static int | ||
568 | isdn_calc_usage(int si, int l2) | ||
569 | { | ||
570 | int usg = ISDN_USAGE_MODEM; | ||
571 | |||
572 | #ifdef CONFIG_ISDN_AUDIO | ||
573 | if (si == 1) { | ||
574 | switch (l2) { | ||
575 | case ISDN_PROTO_L2_MODEM: | ||
576 | usg = ISDN_USAGE_MODEM; | ||
577 | break; | ||
578 | #ifdef CONFIG_ISDN_TTY_FAX | ||
579 | case ISDN_PROTO_L2_FAX: | ||
580 | usg = ISDN_USAGE_FAX; | ||
581 | break; | ||
582 | #endif | ||
583 | case ISDN_PROTO_L2_TRANS: | ||
584 | default: | ||
585 | usg = ISDN_USAGE_VOICE; | ||
586 | break; | ||
587 | } | ||
588 | } | ||
589 | #endif | ||
590 | return (usg); | ||
591 | } | ||
592 | |||
593 | /* isdn_tty_dial() performs dialing of a tty an the necessary | ||
594 | * setup of the lower levels before that. | ||
595 | */ | ||
596 | static void | ||
597 | isdn_tty_dial(char *n, modem_info *info, atemu *m) | ||
598 | { | ||
599 | int usg = ISDN_USAGE_MODEM; | ||
600 | int si = 7; | ||
601 | int l2 = m->mdmreg[REG_L2PROT]; | ||
602 | u_long flags; | ||
603 | isdn_ctrl cmd; | ||
604 | int i; | ||
605 | int j; | ||
606 | |||
607 | for (j = 7; j >= 0; j--) | ||
608 | if (m->mdmreg[REG_SI1] & (1 << j)) { | ||
609 | si = bit2si[j]; | ||
610 | break; | ||
611 | } | ||
612 | usg = isdn_calc_usage(si, l2); | ||
613 | #ifdef CONFIG_ISDN_AUDIO | ||
614 | if ((si == 1) && | ||
615 | (l2 != ISDN_PROTO_L2_MODEM) | ||
616 | #ifdef CONFIG_ISDN_TTY_FAX | ||
617 | && (l2 != ISDN_PROTO_L2_FAX) | ||
618 | #endif | ||
619 | ) { | ||
620 | l2 = ISDN_PROTO_L2_TRANS; | ||
621 | usg = ISDN_USAGE_VOICE; | ||
622 | } | ||
623 | #endif | ||
624 | m->mdmreg[REG_SI1I] = si2bit[si]; | ||
625 | spin_lock_irqsave(&dev->lock, flags); | ||
626 | i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn); | ||
627 | if (i < 0) { | ||
628 | spin_unlock_irqrestore(&dev->lock, flags); | ||
629 | isdn_tty_modem_result(RESULT_NO_DIALTONE, info); | ||
630 | } else { | ||
631 | info->isdn_driver = dev->drvmap[i]; | ||
632 | info->isdn_channel = dev->chanmap[i]; | ||
633 | info->drv_index = i; | ||
634 | dev->m_idx[i] = info->line; | ||
635 | dev->usage[i] |= ISDN_USAGE_OUTGOING; | ||
636 | info->last_dir = 1; | ||
637 | strcpy(info->last_num, n); | ||
638 | isdn_info_update(); | ||
639 | spin_unlock_irqrestore(&dev->lock, flags); | ||
640 | cmd.driver = info->isdn_driver; | ||
641 | cmd.arg = info->isdn_channel; | ||
642 | cmd.command = ISDN_CMD_CLREAZ; | ||
643 | isdn_command(&cmd); | ||
644 | strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver)); | ||
645 | cmd.driver = info->isdn_driver; | ||
646 | cmd.command = ISDN_CMD_SETEAZ; | ||
647 | isdn_command(&cmd); | ||
648 | cmd.driver = info->isdn_driver; | ||
649 | cmd.command = ISDN_CMD_SETL2; | ||
650 | info->last_l2 = l2; | ||
651 | cmd.arg = info->isdn_channel + (l2 << 8); | ||
652 | isdn_command(&cmd); | ||
653 | cmd.driver = info->isdn_driver; | ||
654 | cmd.command = ISDN_CMD_SETL3; | ||
655 | cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); | ||
656 | #ifdef CONFIG_ISDN_TTY_FAX | ||
657 | if (l2 == ISDN_PROTO_L2_FAX) { | ||
658 | cmd.parm.fax = info->fax; | ||
659 | info->fax->direction = ISDN_TTY_FAX_CONN_OUT; | ||
660 | } | ||
661 | #endif | ||
662 | isdn_command(&cmd); | ||
663 | cmd.driver = info->isdn_driver; | ||
664 | cmd.arg = info->isdn_channel; | ||
665 | sprintf(cmd.parm.setup.phone, "%s", n); | ||
666 | sprintf(cmd.parm.setup.eazmsn, "%s", | ||
667 | isdn_map_eaz2msn(m->msn, info->isdn_driver)); | ||
668 | cmd.parm.setup.si1 = si; | ||
669 | cmd.parm.setup.si2 = m->mdmreg[REG_SI2]; | ||
670 | cmd.command = ISDN_CMD_DIAL; | ||
671 | info->dialing = 1; | ||
672 | info->emu.carrierwait = 0; | ||
673 | strcpy(dev->num[i], n); | ||
674 | isdn_info_update(); | ||
675 | isdn_command(&cmd); | ||
676 | isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1); | ||
677 | } | ||
678 | } | ||
679 | |||
680 | /* isdn_tty_hangup() disassociates a tty from the real | ||
681 | * ISDN-line (hangup). The usage-status is cleared | ||
682 | * and some cleanup is done also. | ||
683 | */ | ||
684 | void | ||
685 | isdn_tty_modem_hup(modem_info *info, int local) | ||
686 | { | ||
687 | isdn_ctrl cmd; | ||
688 | int di, ch; | ||
689 | |||
690 | if (!info) | ||
691 | return; | ||
692 | |||
693 | di = info->isdn_driver; | ||
694 | ch = info->isdn_channel; | ||
695 | if (di < 0 || ch < 0) | ||
696 | return; | ||
697 | |||
698 | info->isdn_driver = -1; | ||
699 | info->isdn_channel = -1; | ||
700 | |||
701 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
702 | printk(KERN_DEBUG "Mhup ttyI%d\n", info->line); | ||
703 | #endif | ||
704 | info->rcvsched = 0; | ||
705 | isdn_tty_flush_buffer(info->port.tty); | ||
706 | if (info->online) { | ||
707 | info->last_lhup = local; | ||
708 | info->online = 0; | ||
709 | isdn_tty_modem_result(RESULT_NO_CARRIER, info); | ||
710 | } | ||
711 | #ifdef CONFIG_ISDN_AUDIO | ||
712 | info->vonline = 0; | ||
713 | #ifdef CONFIG_ISDN_TTY_FAX | ||
714 | info->faxonline = 0; | ||
715 | info->fax->phase = ISDN_FAX_PHASE_IDLE; | ||
716 | #endif | ||
717 | info->emu.vpar[4] = 0; | ||
718 | info->emu.vpar[5] = 8; | ||
719 | kfree(info->dtmf_state); | ||
720 | info->dtmf_state = NULL; | ||
721 | kfree(info->silence_state); | ||
722 | info->silence_state = NULL; | ||
723 | kfree(info->adpcms); | ||
724 | info->adpcms = NULL; | ||
725 | kfree(info->adpcmr); | ||
726 | info->adpcmr = NULL; | ||
727 | #endif | ||
728 | if ((info->msr & UART_MSR_RI) && | ||
729 | (info->emu.mdmreg[REG_RUNG] & BIT_RUNG)) | ||
730 | isdn_tty_modem_result(RESULT_RUNG, info); | ||
731 | info->msr &= ~(UART_MSR_DCD | UART_MSR_RI); | ||
732 | info->lsr |= UART_LSR_TEMT; | ||
733 | |||
734 | if (local) { | ||
735 | cmd.driver = di; | ||
736 | cmd.command = ISDN_CMD_HANGUP; | ||
737 | cmd.arg = ch; | ||
738 | isdn_command(&cmd); | ||
739 | } | ||
740 | |||
741 | isdn_all_eaz(di, ch); | ||
742 | info->emu.mdmreg[REG_RINGCNT] = 0; | ||
743 | isdn_free_channel(di, ch, 0); | ||
744 | |||
745 | if (info->drv_index >= 0) { | ||
746 | dev->m_idx[info->drv_index] = -1; | ||
747 | info->drv_index = -1; | ||
748 | } | ||
749 | } | ||
750 | |||
751 | /* | ||
752 | * Begin of a CAPI like interface, currently used only for | ||
753 | * supplementary service (CAPI 2.0 part III) | ||
754 | */ | ||
755 | #include <linux/isdn/capicmd.h> | ||
756 | #include <linux/module.h> | ||
757 | |||
758 | int | ||
759 | isdn_tty_capi_facility(capi_msg *cm) { | ||
760 | return (-1); /* dummy */ | ||
761 | } | ||
762 | |||
763 | /* isdn_tty_suspend() tries to suspend the current tty connection | ||
764 | */ | ||
765 | static void | ||
766 | isdn_tty_suspend(char *id, modem_info *info, atemu *m) | ||
767 | { | ||
768 | isdn_ctrl cmd; | ||
769 | |||
770 | int l; | ||
771 | |||
772 | if (!info) | ||
773 | return; | ||
774 | |||
775 | #ifdef ISDN_DEBUG_MODEM_SERVICES | ||
776 | printk(KERN_DEBUG "Msusp ttyI%d\n", info->line); | ||
777 | #endif | ||
778 | l = strlen(id); | ||
779 | if ((info->isdn_driver >= 0)) { | ||
780 | cmd.parm.cmsg.Length = l + 18; | ||
781 | cmd.parm.cmsg.Command = CAPI_FACILITY; | ||
782 | cmd.parm.cmsg.Subcommand = CAPI_REQ; | ||
783 | cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1; | ||
784 | cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */ | ||
785 | cmd.parm.cmsg.para[1] = 0; | ||
786 | cmd.parm.cmsg.para[2] = l + 3; | ||
787 | cmd.parm.cmsg.para[3] = 4; /* 16 bit 0x0004 Suspend */ | ||
788 | cmd.parm.cmsg.para[4] = 0; | ||
789 | cmd.parm.cmsg.para[5] = l; | ||
790 | memcpy(&cmd.parm.cmsg.para[6], id, l); | ||
791 | cmd.command = CAPI_PUT_MESSAGE; | ||
792 | cmd.driver = info->isdn_driver; | ||
793 | cmd.arg = info->isdn_channel; | ||
794 | isdn_command(&cmd); | ||
795 | } | ||
796 | } | ||
797 | |||
798 | /* isdn_tty_resume() tries to resume a suspended call | ||
799 | * setup of the lower levels before that. unfortunately here is no | ||
800 | * checking for compatibility of used protocols implemented by Q931 | ||
801 | * It does the same things like isdn_tty_dial, the last command | ||
802 | * is different, may be we can merge it. | ||
803 | */ | ||
804 | |||
805 | static void | ||
806 | isdn_tty_resume(char *id, modem_info *info, atemu *m) | ||
807 | { | ||
808 | int usg = ISDN_USAGE_MODEM; | ||
809 | int si = 7; | ||
810 | int l2 = m->mdmreg[REG_L2PROT]; | ||
811 | isdn_ctrl cmd; | ||
812 | ulong flags; | ||
813 | int i; | ||
814 | int j; | ||
815 | int l; | ||
816 | |||
817 | l = strlen(id); | ||
818 | for (j = 7; j >= 0; j--) | ||
819 | if (m->mdmreg[REG_SI1] & (1 << j)) { | ||
820 | si = bit2si[j]; | ||
821 | break; | ||
822 | } | ||
823 | usg = isdn_calc_usage(si, l2); | ||
824 | #ifdef CONFIG_ISDN_AUDIO | ||
825 | if ((si == 1) && | ||
826 | (l2 != ISDN_PROTO_L2_MODEM) | ||
827 | #ifdef CONFIG_ISDN_TTY_FAX | ||
828 | && (l2 != ISDN_PROTO_L2_FAX) | ||
829 | #endif | ||
830 | ) { | ||
831 | l2 = ISDN_PROTO_L2_TRANS; | ||
832 | usg = ISDN_USAGE_VOICE; | ||
833 | } | ||
834 | #endif | ||
835 | m->mdmreg[REG_SI1I] = si2bit[si]; | ||
836 | spin_lock_irqsave(&dev->lock, flags); | ||
837 | i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn); | ||
838 | if (i < 0) { | ||
839 | spin_unlock_irqrestore(&dev->lock, flags); | ||
840 | isdn_tty_modem_result(RESULT_NO_DIALTONE, info); | ||
841 | } else { | ||
842 | info->isdn_driver = dev->drvmap[i]; | ||
843 | info->isdn_channel = dev->chanmap[i]; | ||
844 | info->drv_index = i; | ||
845 | dev->m_idx[i] = info->line; | ||
846 | dev->usage[i] |= ISDN_USAGE_OUTGOING; | ||
847 | info->last_dir = 1; | ||
848 | // strcpy(info->last_num, n); | ||
849 | isdn_info_update(); | ||
850 | spin_unlock_irqrestore(&dev->lock, flags); | ||
851 | cmd.driver = info->isdn_driver; | ||
852 | cmd.arg = info->isdn_channel; | ||
853 | cmd.command = ISDN_CMD_CLREAZ; | ||
854 | isdn_command(&cmd); | ||
855 | strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver)); | ||
856 | cmd.driver = info->isdn_driver; | ||
857 | cmd.command = ISDN_CMD_SETEAZ; | ||
858 | isdn_command(&cmd); | ||
859 | cmd.driver = info->isdn_driver; | ||
860 | cmd.command = ISDN_CMD_SETL2; | ||
861 | info->last_l2 = l2; | ||
862 | cmd.arg = info->isdn_channel + (l2 << 8); | ||
863 | isdn_command(&cmd); | ||
864 | cmd.driver = info->isdn_driver; | ||
865 | cmd.command = ISDN_CMD_SETL3; | ||
866 | cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); | ||
867 | isdn_command(&cmd); | ||
868 | cmd.driver = info->isdn_driver; | ||
869 | cmd.arg = info->isdn_channel; | ||
870 | cmd.parm.cmsg.Length = l + 18; | ||
871 | cmd.parm.cmsg.Command = CAPI_FACILITY; | ||
872 | cmd.parm.cmsg.Subcommand = CAPI_REQ; | ||
873 | cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1; | ||
874 | cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */ | ||
875 | cmd.parm.cmsg.para[1] = 0; | ||
876 | cmd.parm.cmsg.para[2] = l + 3; | ||
877 | cmd.parm.cmsg.para[3] = 5; /* 16 bit 0x0005 Resume */ | ||
878 | cmd.parm.cmsg.para[4] = 0; | ||
879 | cmd.parm.cmsg.para[5] = l; | ||
880 | memcpy(&cmd.parm.cmsg.para[6], id, l); | ||
881 | cmd.command = CAPI_PUT_MESSAGE; | ||
882 | info->dialing = 1; | ||
883 | // strcpy(dev->num[i], n); | ||
884 | isdn_info_update(); | ||
885 | isdn_command(&cmd); | ||
886 | isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1); | ||
887 | } | ||
888 | } | ||
889 | |||
890 | /* isdn_tty_send_msg() sends a message to a HL driver | ||
891 | * This is used for hybrid modem cards to send AT commands to it | ||
892 | */ | ||
893 | |||
894 | static void | ||
895 | isdn_tty_send_msg(modem_info *info, atemu *m, char *msg) | ||
896 | { | ||
897 | int usg = ISDN_USAGE_MODEM; | ||
898 | int si = 7; | ||
899 | int l2 = m->mdmreg[REG_L2PROT]; | ||
900 | isdn_ctrl cmd; | ||
901 | ulong flags; | ||
902 | int i; | ||
903 | int j; | ||
904 | int l; | ||
905 | |||
906 | l = min(strlen(msg), sizeof(cmd.parm) - sizeof(cmd.parm.cmsg) | ||
907 | + sizeof(cmd.parm.cmsg.para) - 2); | ||
908 | |||
909 | if (!l) { | ||
910 | isdn_tty_modem_result(RESULT_ERROR, info); | ||
911 | return; | ||
912 | } | ||
913 | for (j = 7; j >= 0; j--) | ||
914 | if (m->mdmreg[REG_SI1] & (1 << j)) { | ||
915 | si = bit2si[j]; | ||
916 | break; | ||
917 | } | ||
918 | usg = isdn_calc_usage(si, l2); | ||
919 | #ifdef CONFIG_ISDN_AUDIO | ||
920 | if ((si == 1) && | ||
921 | (l2 != ISDN_PROTO_L2_MODEM) | ||
922 | #ifdef CONFIG_ISDN_TTY_FAX | ||
923 | && (l2 != ISDN_PROTO_L2_FAX) | ||
924 | #endif | ||
925 | ) { | ||
926 | l2 = ISDN_PROTO_L2_TRANS; | ||
927 | usg = ISDN_USAGE_VOICE; | ||
928 | } | ||
929 | #endif | ||
930 | m->mdmreg[REG_SI1I] = si2bit[si]; | ||
931 | spin_lock_irqsave(&dev->lock, flags); | ||
932 | i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn); | ||
933 | if (i < 0) { | ||
934 | spin_unlock_irqrestore(&dev->lock, flags); | ||
935 | isdn_tty_modem_result(RESULT_NO_DIALTONE, info); | ||
936 | } else { | ||
937 | info->isdn_driver = dev->drvmap[i]; | ||
938 | info->isdn_channel = dev->chanmap[i]; | ||
939 | info->drv_index = i; | ||
940 | dev->m_idx[i] = info->line; | ||
941 | dev->usage[i] |= ISDN_USAGE_OUTGOING; | ||
942 | info->last_dir = 1; | ||
943 | isdn_info_update(); | ||
944 | spin_unlock_irqrestore(&dev->lock, flags); | ||
945 | cmd.driver = info->isdn_driver; | ||
946 | cmd.arg = info->isdn_channel; | ||
947 | cmd.command = ISDN_CMD_CLREAZ; | ||
948 | isdn_command(&cmd); | ||
949 | strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver)); | ||
950 | cmd.driver = info->isdn_driver; | ||
951 | cmd.command = ISDN_CMD_SETEAZ; | ||
952 | isdn_command(&cmd); | ||
953 | cmd.driver = info->isdn_driver; | ||
954 | cmd.command = ISDN_CMD_SETL2; | ||
955 | info->last_l2 = l2; | ||
956 | cmd.arg = info->isdn_channel + (l2 << 8); | ||
957 | isdn_command(&cmd); | ||
958 | cmd.driver = info->isdn_driver; | ||
959 | cmd.command = ISDN_CMD_SETL3; | ||
960 | cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); | ||
961 | isdn_command(&cmd); | ||
962 | cmd.driver = info->isdn_driver; | ||
963 | cmd.arg = info->isdn_channel; | ||
964 | cmd.parm.cmsg.Length = l + 14; | ||
965 | cmd.parm.cmsg.Command = CAPI_MANUFACTURER; | ||
966 | cmd.parm.cmsg.Subcommand = CAPI_REQ; | ||
967 | cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1; | ||
968 | cmd.parm.cmsg.para[0] = l + 1; | ||
969 | strncpy(&cmd.parm.cmsg.para[1], msg, l); | ||
970 | cmd.parm.cmsg.para[l + 1] = 0xd; | ||
971 | cmd.command = CAPI_PUT_MESSAGE; | ||
972 | /* info->dialing = 1; | ||
973 | strcpy(dev->num[i], n); | ||
974 | isdn_info_update(); | ||
975 | */ | ||
976 | isdn_command(&cmd); | ||
977 | } | ||
978 | } | ||
979 | |||
980 | static inline int | ||
981 | isdn_tty_paranoia_check(modem_info *info, char *name, const char *routine) | ||
982 | { | ||
983 | #ifdef MODEM_PARANOIA_CHECK | ||
984 | if (!info) { | ||
985 | printk(KERN_WARNING "isdn_tty: null info_struct for %s in %s\n", | ||
986 | name, routine); | ||
987 | return 1; | ||
988 | } | ||
989 | if (info->magic != ISDN_ASYNC_MAGIC) { | ||
990 | printk(KERN_WARNING "isdn_tty: bad magic for modem struct %s in %s\n", | ||
991 | name, routine); | ||
992 | return 1; | ||
993 | } | ||
994 | #endif | ||
995 | return 0; | ||
996 | } | ||
997 | |||
998 | /* | ||
999 | * This routine is called to set the UART divisor registers to match | ||
1000 | * the specified baud rate for a serial port. | ||
1001 | */ | ||
1002 | static void | ||
1003 | isdn_tty_change_speed(modem_info *info) | ||
1004 | { | ||
1005 | struct tty_port *port = &info->port; | ||
1006 | uint cflag, | ||
1007 | cval, | ||
1008 | quot; | ||
1009 | int i; | ||
1010 | |||
1011 | if (!port->tty) | ||
1012 | return; | ||
1013 | cflag = port->tty->termios.c_cflag; | ||
1014 | |||
1015 | quot = i = cflag & CBAUD; | ||
1016 | if (i & CBAUDEX) { | ||
1017 | i &= ~CBAUDEX; | ||
1018 | if (i < 1 || i > 2) | ||
1019 | port->tty->termios.c_cflag &= ~CBAUDEX; | ||
1020 | else | ||
1021 | i += 15; | ||
1022 | } | ||
1023 | if (quot) { | ||
1024 | info->mcr |= UART_MCR_DTR; | ||
1025 | isdn_tty_modem_ncarrier(info); | ||
1026 | } else { | ||
1027 | info->mcr &= ~UART_MCR_DTR; | ||
1028 | if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { | ||
1029 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
1030 | printk(KERN_DEBUG "Mhup in changespeed\n"); | ||
1031 | #endif | ||
1032 | if (info->online) | ||
1033 | info->ncarrier = 1; | ||
1034 | isdn_tty_modem_reset_regs(info, 0); | ||
1035 | isdn_tty_modem_hup(info, 1); | ||
1036 | } | ||
1037 | return; | ||
1038 | } | ||
1039 | /* byte size and parity */ | ||
1040 | cval = cflag & (CSIZE | CSTOPB); | ||
1041 | cval >>= 4; | ||
1042 | if (cflag & PARENB) | ||
1043 | cval |= UART_LCR_PARITY; | ||
1044 | if (!(cflag & PARODD)) | ||
1045 | cval |= UART_LCR_EPAR; | ||
1046 | |||
1047 | tty_port_set_check_carrier(port, ~cflag & CLOCAL); | ||
1048 | } | ||
1049 | |||
1050 | static int | ||
1051 | isdn_tty_startup(modem_info *info) | ||
1052 | { | ||
1053 | if (tty_port_initialized(&info->port)) | ||
1054 | return 0; | ||
1055 | isdn_lock_drivers(); | ||
1056 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1057 | printk(KERN_DEBUG "starting up ttyi%d ...\n", info->line); | ||
1058 | #endif | ||
1059 | /* | ||
1060 | * Now, initialize the UART | ||
1061 | */ | ||
1062 | info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2; | ||
1063 | if (info->port.tty) | ||
1064 | clear_bit(TTY_IO_ERROR, &info->port.tty->flags); | ||
1065 | /* | ||
1066 | * and set the speed of the serial port | ||
1067 | */ | ||
1068 | isdn_tty_change_speed(info); | ||
1069 | |||
1070 | tty_port_set_initialized(&info->port, 1); | ||
1071 | info->msr |= (UART_MSR_DSR | UART_MSR_CTS); | ||
1072 | info->send_outstanding = 0; | ||
1073 | return 0; | ||
1074 | } | ||
1075 | |||
1076 | /* | ||
1077 | * This routine will shutdown a serial port; interrupts are disabled, and | ||
1078 | * DTR is dropped if the hangup on close termio flag is on. | ||
1079 | */ | ||
1080 | static void | ||
1081 | isdn_tty_shutdown(modem_info *info) | ||
1082 | { | ||
1083 | if (!tty_port_initialized(&info->port)) | ||
1084 | return; | ||
1085 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1086 | printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line); | ||
1087 | #endif | ||
1088 | isdn_unlock_drivers(); | ||
1089 | info->msr &= ~UART_MSR_RI; | ||
1090 | if (!info->port.tty || (info->port.tty->termios.c_cflag & HUPCL)) { | ||
1091 | info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS); | ||
1092 | if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { | ||
1093 | isdn_tty_modem_reset_regs(info, 0); | ||
1094 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
1095 | printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n"); | ||
1096 | #endif | ||
1097 | isdn_tty_modem_hup(info, 1); | ||
1098 | } | ||
1099 | } | ||
1100 | if (info->port.tty) | ||
1101 | set_bit(TTY_IO_ERROR, &info->port.tty->flags); | ||
1102 | |||
1103 | tty_port_set_initialized(&info->port, 0); | ||
1104 | } | ||
1105 | |||
1106 | /* isdn_tty_write() is the main send-routine. It is called from the upper | ||
1107 | * levels within the kernel to perform sending data. Depending on the | ||
1108 | * online-flag it either directs output to the at-command-interpreter or | ||
1109 | * to the lower level. Additional tasks done here: | ||
1110 | * - If online, check for escape-sequence (+++) | ||
1111 | * - If sending audio-data, call isdn_tty_DLEdown() to parse DLE-codes. | ||
1112 | * - If receiving audio-data, call isdn_tty_end_vrx() to abort if needed. | ||
1113 | * - If dialing, abort dial. | ||
1114 | */ | ||
1115 | static int | ||
1116 | isdn_tty_write(struct tty_struct *tty, const u_char *buf, int count) | ||
1117 | { | ||
1118 | int c; | ||
1119 | int total = 0; | ||
1120 | modem_info *info = (modem_info *) tty->driver_data; | ||
1121 | atemu *m = &info->emu; | ||
1122 | |||
1123 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_write")) | ||
1124 | return 0; | ||
1125 | /* See isdn_tty_senddown() */ | ||
1126 | atomic_inc(&info->xmit_lock); | ||
1127 | while (1) { | ||
1128 | c = count; | ||
1129 | if (c > info->xmit_size - info->xmit_count) | ||
1130 | c = info->xmit_size - info->xmit_count; | ||
1131 | if (info->isdn_driver >= 0 && c > dev->drv[info->isdn_driver]->maxbufsize) | ||
1132 | c = dev->drv[info->isdn_driver]->maxbufsize; | ||
1133 | if (c <= 0) | ||
1134 | break; | ||
1135 | if ((info->online > 1) | ||
1136 | #ifdef CONFIG_ISDN_AUDIO | ||
1137 | || (info->vonline & 3) | ||
1138 | #endif | ||
1139 | ) { | ||
1140 | #ifdef CONFIG_ISDN_AUDIO | ||
1141 | if (!info->vonline) | ||
1142 | #endif | ||
1143 | isdn_tty_check_esc(buf, m->mdmreg[REG_ESC], c, | ||
1144 | &(m->pluscount), | ||
1145 | &(m->lastplus)); | ||
1146 | memcpy(&info->port.xmit_buf[info->xmit_count], buf, c); | ||
1147 | #ifdef CONFIG_ISDN_AUDIO | ||
1148 | if (info->vonline) { | ||
1149 | int cc = isdn_tty_handleDLEdown(info, m, c); | ||
1150 | if (info->vonline & 2) { | ||
1151 | if (!cc) { | ||
1152 | /* If DLE decoding results in zero-transmit, but | ||
1153 | * c originally was non-zero, do a wakeup. | ||
1154 | */ | ||
1155 | tty_wakeup(tty); | ||
1156 | info->msr |= UART_MSR_CTS; | ||
1157 | info->lsr |= UART_LSR_TEMT; | ||
1158 | } | ||
1159 | info->xmit_count += cc; | ||
1160 | } | ||
1161 | if ((info->vonline & 3) == 1) { | ||
1162 | /* Do NOT handle Ctrl-Q or Ctrl-S | ||
1163 | * when in full-duplex audio mode. | ||
1164 | */ | ||
1165 | if (isdn_tty_end_vrx(buf, c)) { | ||
1166 | info->vonline &= ~1; | ||
1167 | #ifdef ISDN_DEBUG_MODEM_VOICE | ||
1168 | printk(KERN_DEBUG | ||
1169 | "got !^Q/^S, send DLE-ETX,VCON on ttyI%d\n", | ||
1170 | info->line); | ||
1171 | #endif | ||
1172 | isdn_tty_at_cout("\020\003\r\nVCON\r\n", info); | ||
1173 | } | ||
1174 | } | ||
1175 | } else | ||
1176 | if (TTY_IS_FCLASS1(info)) { | ||
1177 | int cc = isdn_tty_handleDLEdown(info, m, c); | ||
1178 | |||
1179 | if (info->vonline & 4) { /* ETX seen */ | ||
1180 | isdn_ctrl c; | ||
1181 | |||
1182 | c.command = ISDN_CMD_FAXCMD; | ||
1183 | c.driver = info->isdn_driver; | ||
1184 | c.arg = info->isdn_channel; | ||
1185 | c.parm.aux.cmd = ISDN_FAX_CLASS1_CTRL; | ||
1186 | c.parm.aux.subcmd = ETX; | ||
1187 | isdn_command(&c); | ||
1188 | } | ||
1189 | info->vonline = 0; | ||
1190 | #ifdef ISDN_DEBUG_MODEM_VOICE | ||
1191 | printk(KERN_DEBUG "fax dle cc/c %d/%d\n", cc, c); | ||
1192 | #endif | ||
1193 | info->xmit_count += cc; | ||
1194 | } else | ||
1195 | #endif | ||
1196 | info->xmit_count += c; | ||
1197 | } else { | ||
1198 | info->msr |= UART_MSR_CTS; | ||
1199 | info->lsr |= UART_LSR_TEMT; | ||
1200 | if (info->dialing) { | ||
1201 | info->dialing = 0; | ||
1202 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
1203 | printk(KERN_DEBUG "Mhup in isdn_tty_write\n"); | ||
1204 | #endif | ||
1205 | isdn_tty_modem_result(RESULT_NO_CARRIER, info); | ||
1206 | isdn_tty_modem_hup(info, 1); | ||
1207 | } else | ||
1208 | c = isdn_tty_edit_at(buf, c, info); | ||
1209 | } | ||
1210 | buf += c; | ||
1211 | count -= c; | ||
1212 | total += c; | ||
1213 | } | ||
1214 | atomic_dec(&info->xmit_lock); | ||
1215 | if ((info->xmit_count) || !skb_queue_empty(&info->xmit_queue)) { | ||
1216 | if (m->mdmreg[REG_DXMT] & BIT_DXMT) { | ||
1217 | isdn_tty_senddown(info); | ||
1218 | isdn_tty_tint(info); | ||
1219 | } | ||
1220 | isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1); | ||
1221 | } | ||
1222 | return total; | ||
1223 | } | ||
1224 | |||
1225 | static int | ||
1226 | isdn_tty_write_room(struct tty_struct *tty) | ||
1227 | { | ||
1228 | modem_info *info = (modem_info *) tty->driver_data; | ||
1229 | int ret; | ||
1230 | |||
1231 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_write_room")) | ||
1232 | return 0; | ||
1233 | if (!info->online) | ||
1234 | return info->xmit_size; | ||
1235 | ret = info->xmit_size - info->xmit_count; | ||
1236 | return (ret < 0) ? 0 : ret; | ||
1237 | } | ||
1238 | |||
1239 | static int | ||
1240 | isdn_tty_chars_in_buffer(struct tty_struct *tty) | ||
1241 | { | ||
1242 | modem_info *info = (modem_info *) tty->driver_data; | ||
1243 | |||
1244 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_chars_in_buffer")) | ||
1245 | return 0; | ||
1246 | if (!info->online) | ||
1247 | return 0; | ||
1248 | return (info->xmit_count); | ||
1249 | } | ||
1250 | |||
1251 | static void | ||
1252 | isdn_tty_flush_buffer(struct tty_struct *tty) | ||
1253 | { | ||
1254 | modem_info *info; | ||
1255 | |||
1256 | if (!tty) { | ||
1257 | return; | ||
1258 | } | ||
1259 | info = (modem_info *) tty->driver_data; | ||
1260 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_flush_buffer")) { | ||
1261 | return; | ||
1262 | } | ||
1263 | isdn_tty_cleanup_xmit(info); | ||
1264 | info->xmit_count = 0; | ||
1265 | tty_wakeup(tty); | ||
1266 | } | ||
1267 | |||
1268 | static void | ||
1269 | isdn_tty_flush_chars(struct tty_struct *tty) | ||
1270 | { | ||
1271 | modem_info *info = (modem_info *) tty->driver_data; | ||
1272 | |||
1273 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_flush_chars")) | ||
1274 | return; | ||
1275 | if ((info->xmit_count) || !skb_queue_empty(&info->xmit_queue)) | ||
1276 | isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1); | ||
1277 | } | ||
1278 | |||
1279 | /* | ||
1280 | * ------------------------------------------------------------ | ||
1281 | * isdn_tty_throttle() | ||
1282 | * | ||
1283 | * This routine is called by the upper-layer tty layer to signal that | ||
1284 | * incoming characters should be throttled. | ||
1285 | * ------------------------------------------------------------ | ||
1286 | */ | ||
1287 | static void | ||
1288 | isdn_tty_throttle(struct tty_struct *tty) | ||
1289 | { | ||
1290 | modem_info *info = (modem_info *) tty->driver_data; | ||
1291 | |||
1292 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_throttle")) | ||
1293 | return; | ||
1294 | if (I_IXOFF(tty)) | ||
1295 | info->x_char = STOP_CHAR(tty); | ||
1296 | info->mcr &= ~UART_MCR_RTS; | ||
1297 | } | ||
1298 | |||
1299 | static void | ||
1300 | isdn_tty_unthrottle(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_unthrottle")) | ||
1305 | return; | ||
1306 | if (I_IXOFF(tty)) { | ||
1307 | if (info->x_char) | ||
1308 | info->x_char = 0; | ||
1309 | else | ||
1310 | info->x_char = START_CHAR(tty); | ||
1311 | } | ||
1312 | info->mcr |= UART_MCR_RTS; | ||
1313 | } | ||
1314 | |||
1315 | /* | ||
1316 | * ------------------------------------------------------------ | ||
1317 | * isdn_tty_ioctl() and friends | ||
1318 | * ------------------------------------------------------------ | ||
1319 | */ | ||
1320 | |||
1321 | /* | ||
1322 | * isdn_tty_get_lsr_info - get line status register info | ||
1323 | * | ||
1324 | * Purpose: Let user call ioctl() to get info when the UART physically | ||
1325 | * is emptied. On bus types like RS485, the transmitter must | ||
1326 | * release the bus after transmitting. This must be done when | ||
1327 | * the transmit shift register is empty, not be done when the | ||
1328 | * transmit holding register is empty. This functionality | ||
1329 | * allows RS485 driver to be written in user space. | ||
1330 | */ | ||
1331 | static int | ||
1332 | isdn_tty_get_lsr_info(modem_info *info, uint __user *value) | ||
1333 | { | ||
1334 | u_char status; | ||
1335 | uint result; | ||
1336 | |||
1337 | status = info->lsr; | ||
1338 | result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); | ||
1339 | return put_user(result, value); | ||
1340 | } | ||
1341 | |||
1342 | |||
1343 | static int | ||
1344 | isdn_tty_tiocmget(struct tty_struct *tty) | ||
1345 | { | ||
1346 | modem_info *info = (modem_info *) tty->driver_data; | ||
1347 | u_char control, status; | ||
1348 | |||
1349 | if (isdn_tty_paranoia_check(info, tty->name, __func__)) | ||
1350 | return -ENODEV; | ||
1351 | if (tty_io_error(tty)) | ||
1352 | return -EIO; | ||
1353 | |||
1354 | mutex_lock(&modem_info_mutex); | ||
1355 | #ifdef ISDN_DEBUG_MODEM_IOCTL | ||
1356 | printk(KERN_DEBUG "ttyI%d ioctl TIOCMGET\n", info->line); | ||
1357 | #endif | ||
1358 | |||
1359 | control = info->mcr; | ||
1360 | status = info->msr; | ||
1361 | mutex_unlock(&modem_info_mutex); | ||
1362 | return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | ||
1363 | | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | ||
1364 | | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) | ||
1365 | | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) | ||
1366 | | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) | ||
1367 | | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); | ||
1368 | } | ||
1369 | |||
1370 | static int | ||
1371 | isdn_tty_tiocmset(struct tty_struct *tty, | ||
1372 | unsigned int set, unsigned int clear) | ||
1373 | { | ||
1374 | modem_info *info = (modem_info *) tty->driver_data; | ||
1375 | |||
1376 | if (isdn_tty_paranoia_check(info, tty->name, __func__)) | ||
1377 | return -ENODEV; | ||
1378 | if (tty_io_error(tty)) | ||
1379 | return -EIO; | ||
1380 | |||
1381 | #ifdef ISDN_DEBUG_MODEM_IOCTL | ||
1382 | printk(KERN_DEBUG "ttyI%d ioctl TIOCMxxx: %x %x\n", info->line, set, clear); | ||
1383 | #endif | ||
1384 | |||
1385 | mutex_lock(&modem_info_mutex); | ||
1386 | if (set & TIOCM_RTS) | ||
1387 | info->mcr |= UART_MCR_RTS; | ||
1388 | if (set & TIOCM_DTR) { | ||
1389 | info->mcr |= UART_MCR_DTR; | ||
1390 | isdn_tty_modem_ncarrier(info); | ||
1391 | } | ||
1392 | |||
1393 | if (clear & TIOCM_RTS) | ||
1394 | info->mcr &= ~UART_MCR_RTS; | ||
1395 | if (clear & TIOCM_DTR) { | ||
1396 | info->mcr &= ~UART_MCR_DTR; | ||
1397 | if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { | ||
1398 | isdn_tty_modem_reset_regs(info, 0); | ||
1399 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
1400 | printk(KERN_DEBUG "Mhup in TIOCMSET\n"); | ||
1401 | #endif | ||
1402 | if (info->online) | ||
1403 | info->ncarrier = 1; | ||
1404 | isdn_tty_modem_hup(info, 1); | ||
1405 | } | ||
1406 | } | ||
1407 | mutex_unlock(&modem_info_mutex); | ||
1408 | return 0; | ||
1409 | } | ||
1410 | |||
1411 | static int | ||
1412 | isdn_tty_ioctl(struct tty_struct *tty, uint cmd, ulong arg) | ||
1413 | { | ||
1414 | modem_info *info = (modem_info *) tty->driver_data; | ||
1415 | |||
1416 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_ioctl")) | ||
1417 | return -ENODEV; | ||
1418 | if (tty_io_error(tty)) | ||
1419 | return -EIO; | ||
1420 | switch (cmd) { | ||
1421 | case TIOCSERGETLSR: /* Get line status register */ | ||
1422 | #ifdef ISDN_DEBUG_MODEM_IOCTL | ||
1423 | printk(KERN_DEBUG "ttyI%d ioctl TIOCSERGETLSR\n", info->line); | ||
1424 | #endif | ||
1425 | return isdn_tty_get_lsr_info(info, (uint __user *) arg); | ||
1426 | default: | ||
1427 | #ifdef ISDN_DEBUG_MODEM_IOCTL | ||
1428 | printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line); | ||
1429 | #endif | ||
1430 | return -ENOIOCTLCMD; | ||
1431 | } | ||
1432 | return 0; | ||
1433 | } | ||
1434 | |||
1435 | static void | ||
1436 | isdn_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios) | ||
1437 | { | ||
1438 | modem_info *info = (modem_info *) tty->driver_data; | ||
1439 | |||
1440 | mutex_lock(&modem_info_mutex); | ||
1441 | if (!old_termios) | ||
1442 | isdn_tty_change_speed(info); | ||
1443 | else { | ||
1444 | if (tty->termios.c_cflag == old_termios->c_cflag && | ||
1445 | tty->termios.c_ispeed == old_termios->c_ispeed && | ||
1446 | tty->termios.c_ospeed == old_termios->c_ospeed) { | ||
1447 | mutex_unlock(&modem_info_mutex); | ||
1448 | return; | ||
1449 | } | ||
1450 | isdn_tty_change_speed(info); | ||
1451 | } | ||
1452 | mutex_unlock(&modem_info_mutex); | ||
1453 | } | ||
1454 | |||
1455 | /* | ||
1456 | * ------------------------------------------------------------ | ||
1457 | * isdn_tty_open() and friends | ||
1458 | * ------------------------------------------------------------ | ||
1459 | */ | ||
1460 | |||
1461 | static int isdn_tty_install(struct tty_driver *driver, struct tty_struct *tty) | ||
1462 | { | ||
1463 | modem_info *info = &dev->mdm.info[tty->index]; | ||
1464 | |||
1465 | if (isdn_tty_paranoia_check(info, tty->name, __func__)) | ||
1466 | return -ENODEV; | ||
1467 | |||
1468 | tty->driver_data = info; | ||
1469 | |||
1470 | return tty_port_install(&info->port, driver, tty); | ||
1471 | } | ||
1472 | |||
1473 | /* | ||
1474 | * This routine is called whenever a serial port is opened. It | ||
1475 | * enables interrupts for a serial port, linking in its async structure into | ||
1476 | * the IRQ chain. It also performs the serial-specific | ||
1477 | * initialization for the tty structure. | ||
1478 | */ | ||
1479 | static int | ||
1480 | isdn_tty_open(struct tty_struct *tty, struct file *filp) | ||
1481 | { | ||
1482 | modem_info *info = tty->driver_data; | ||
1483 | struct tty_port *port = &info->port; | ||
1484 | int retval; | ||
1485 | |||
1486 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1487 | printk(KERN_DEBUG "isdn_tty_open %s, count = %d\n", tty->name, | ||
1488 | port->count); | ||
1489 | #endif | ||
1490 | port->count++; | ||
1491 | port->tty = tty; | ||
1492 | /* | ||
1493 | * Start up serial port | ||
1494 | */ | ||
1495 | retval = isdn_tty_startup(info); | ||
1496 | if (retval) { | ||
1497 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1498 | printk(KERN_DEBUG "isdn_tty_open return after startup\n"); | ||
1499 | #endif | ||
1500 | return retval; | ||
1501 | } | ||
1502 | retval = tty_port_block_til_ready(port, tty, filp); | ||
1503 | if (retval) { | ||
1504 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1505 | printk(KERN_DEBUG "isdn_tty_open return after isdn_tty_block_til_ready \n"); | ||
1506 | #endif | ||
1507 | return retval; | ||
1508 | } | ||
1509 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1510 | printk(KERN_DEBUG "isdn_tty_open ttyi%d successful...\n", info->line); | ||
1511 | #endif | ||
1512 | dev->modempoll++; | ||
1513 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1514 | printk(KERN_DEBUG "isdn_tty_open normal exit\n"); | ||
1515 | #endif | ||
1516 | return 0; | ||
1517 | } | ||
1518 | |||
1519 | static void | ||
1520 | isdn_tty_close(struct tty_struct *tty, struct file *filp) | ||
1521 | { | ||
1522 | modem_info *info = (modem_info *) tty->driver_data; | ||
1523 | struct tty_port *port = &info->port; | ||
1524 | ulong timeout; | ||
1525 | |||
1526 | if (!info || isdn_tty_paranoia_check(info, tty->name, "isdn_tty_close")) | ||
1527 | return; | ||
1528 | if (tty_hung_up_p(filp)) { | ||
1529 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1530 | printk(KERN_DEBUG "isdn_tty_close return after tty_hung_up_p\n"); | ||
1531 | #endif | ||
1532 | return; | ||
1533 | } | ||
1534 | if ((tty->count == 1) && (port->count != 1)) { | ||
1535 | /* | ||
1536 | * Uh, oh. tty->count is 1, which means that the tty | ||
1537 | * structure will be freed. Info->count should always | ||
1538 | * be one in these conditions. If it's greater than | ||
1539 | * one, we've got real problems, since it means the | ||
1540 | * serial port won't be shutdown. | ||
1541 | */ | ||
1542 | printk(KERN_ERR "isdn_tty_close: bad port count; tty->count is 1, " | ||
1543 | "info->count is %d\n", port->count); | ||
1544 | port->count = 1; | ||
1545 | } | ||
1546 | if (--port->count < 0) { | ||
1547 | printk(KERN_ERR "isdn_tty_close: bad port count for ttyi%d: %d\n", | ||
1548 | info->line, port->count); | ||
1549 | port->count = 0; | ||
1550 | } | ||
1551 | if (port->count) { | ||
1552 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1553 | printk(KERN_DEBUG "isdn_tty_close after info->count != 0\n"); | ||
1554 | #endif | ||
1555 | return; | ||
1556 | } | ||
1557 | info->closing = 1; | ||
1558 | |||
1559 | tty->closing = 1; | ||
1560 | /* | ||
1561 | * At this point we stop accepting input. To do this, we | ||
1562 | * disable the receive line status interrupts, and tell the | ||
1563 | * interrupt driver to stop checking the data ready bit in the | ||
1564 | * line status register. | ||
1565 | */ | ||
1566 | if (tty_port_initialized(port)) { | ||
1567 | tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ | ||
1568 | /* | ||
1569 | * Before we drop DTR, make sure the UART transmitter | ||
1570 | * has completely drained; this is especially | ||
1571 | * important if there is a transmit FIFO! | ||
1572 | */ | ||
1573 | timeout = jiffies + HZ; | ||
1574 | while (!(info->lsr & UART_LSR_TEMT)) { | ||
1575 | schedule_timeout_interruptible(20); | ||
1576 | if (time_after(jiffies, timeout)) | ||
1577 | break; | ||
1578 | } | ||
1579 | } | ||
1580 | dev->modempoll--; | ||
1581 | isdn_tty_shutdown(info); | ||
1582 | isdn_tty_flush_buffer(tty); | ||
1583 | tty_ldisc_flush(tty); | ||
1584 | port->tty = NULL; | ||
1585 | info->ncarrier = 0; | ||
1586 | |||
1587 | tty_port_close_end(port, tty); | ||
1588 | info->closing = 0; | ||
1589 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1590 | printk(KERN_DEBUG "isdn_tty_close normal exit\n"); | ||
1591 | #endif | ||
1592 | } | ||
1593 | |||
1594 | /* | ||
1595 | * isdn_tty_hangup() --- called by tty_hangup() when a hangup is signaled. | ||
1596 | */ | ||
1597 | static void | ||
1598 | isdn_tty_hangup(struct tty_struct *tty) | ||
1599 | { | ||
1600 | modem_info *info = (modem_info *) tty->driver_data; | ||
1601 | struct tty_port *port = &info->port; | ||
1602 | |||
1603 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_hangup")) | ||
1604 | return; | ||
1605 | isdn_tty_shutdown(info); | ||
1606 | port->count = 0; | ||
1607 | tty_port_set_active(port, 0); | ||
1608 | port->tty = NULL; | ||
1609 | wake_up_interruptible(&port->open_wait); | ||
1610 | } | ||
1611 | |||
1612 | /* This routine initializes all emulator-data. | ||
1613 | */ | ||
1614 | static void | ||
1615 | isdn_tty_reset_profile(atemu *m) | ||
1616 | { | ||
1617 | m->profile[0] = 0; | ||
1618 | m->profile[1] = 0; | ||
1619 | m->profile[2] = 43; | ||
1620 | m->profile[3] = 13; | ||
1621 | m->profile[4] = 10; | ||
1622 | m->profile[5] = 8; | ||
1623 | m->profile[6] = 3; | ||
1624 | m->profile[7] = 60; | ||
1625 | m->profile[8] = 2; | ||
1626 | m->profile[9] = 6; | ||
1627 | m->profile[10] = 7; | ||
1628 | m->profile[11] = 70; | ||
1629 | m->profile[12] = 0x45; | ||
1630 | m->profile[13] = 4; | ||
1631 | m->profile[14] = ISDN_PROTO_L2_X75I; | ||
1632 | m->profile[15] = ISDN_PROTO_L3_TRANS; | ||
1633 | m->profile[16] = ISDN_SERIAL_XMIT_SIZE / 16; | ||
1634 | m->profile[17] = ISDN_MODEM_WINSIZE; | ||
1635 | m->profile[18] = 4; | ||
1636 | m->profile[19] = 0; | ||
1637 | m->profile[20] = 0; | ||
1638 | m->profile[23] = 0; | ||
1639 | m->pmsn[0] = '\0'; | ||
1640 | m->plmsn[0] = '\0'; | ||
1641 | } | ||
1642 | |||
1643 | #ifdef CONFIG_ISDN_AUDIO | ||
1644 | static void | ||
1645 | isdn_tty_modem_reset_vpar(atemu *m) | ||
1646 | { | ||
1647 | m->vpar[0] = 2; /* Voice-device (2 = phone line) */ | ||
1648 | m->vpar[1] = 0; /* Silence detection level (0 = none ) */ | ||
1649 | m->vpar[2] = 70; /* Silence interval (7 sec. ) */ | ||
1650 | m->vpar[3] = 2; /* Compression type (1 = ADPCM-2 ) */ | ||
1651 | m->vpar[4] = 0; /* DTMF detection level (0 = softcode ) */ | ||
1652 | m->vpar[5] = 8; /* DTMF interval (8 * 5 ms. ) */ | ||
1653 | } | ||
1654 | #endif | ||
1655 | |||
1656 | #ifdef CONFIG_ISDN_TTY_FAX | ||
1657 | static void | ||
1658 | isdn_tty_modem_reset_faxpar(modem_info *info) | ||
1659 | { | ||
1660 | T30_s *f = info->fax; | ||
1661 | |||
1662 | f->code = 0; | ||
1663 | f->phase = ISDN_FAX_PHASE_IDLE; | ||
1664 | f->direction = 0; | ||
1665 | f->resolution = 1; /* fine */ | ||
1666 | f->rate = 5; /* 14400 bit/s */ | ||
1667 | f->width = 0; | ||
1668 | f->length = 0; | ||
1669 | f->compression = 0; | ||
1670 | f->ecm = 0; | ||
1671 | f->binary = 0; | ||
1672 | f->scantime = 0; | ||
1673 | memset(&f->id[0], 32, FAXIDLEN - 1); | ||
1674 | f->id[FAXIDLEN - 1] = 0; | ||
1675 | f->badlin = 0; | ||
1676 | f->badmul = 0; | ||
1677 | f->bor = 0; | ||
1678 | f->nbc = 0; | ||
1679 | f->cq = 0; | ||
1680 | f->cr = 0; | ||
1681 | f->ctcrty = 0; | ||
1682 | f->minsp = 0; | ||
1683 | f->phcto = 30; | ||
1684 | f->rel = 0; | ||
1685 | memset(&f->pollid[0], 32, FAXIDLEN - 1); | ||
1686 | f->pollid[FAXIDLEN - 1] = 0; | ||
1687 | } | ||
1688 | #endif | ||
1689 | |||
1690 | static void | ||
1691 | isdn_tty_modem_reset_regs(modem_info *info, int force) | ||
1692 | { | ||
1693 | atemu *m = &info->emu; | ||
1694 | if ((m->mdmreg[REG_DTRR] & BIT_DTRR) || force) { | ||
1695 | memcpy(m->mdmreg, m->profile, ISDN_MODEM_NUMREG); | ||
1696 | memcpy(m->msn, m->pmsn, ISDN_MSNLEN); | ||
1697 | memcpy(m->lmsn, m->plmsn, ISDN_LMSNLEN); | ||
1698 | info->xmit_size = m->mdmreg[REG_PSIZE] * 16; | ||
1699 | } | ||
1700 | #ifdef CONFIG_ISDN_AUDIO | ||
1701 | isdn_tty_modem_reset_vpar(m); | ||
1702 | #endif | ||
1703 | #ifdef CONFIG_ISDN_TTY_FAX | ||
1704 | isdn_tty_modem_reset_faxpar(info); | ||
1705 | #endif | ||
1706 | m->mdmcmdl = 0; | ||
1707 | } | ||
1708 | |||
1709 | static void | ||
1710 | modem_write_profile(atemu *m) | ||
1711 | { | ||
1712 | memcpy(m->profile, m->mdmreg, ISDN_MODEM_NUMREG); | ||
1713 | memcpy(m->pmsn, m->msn, ISDN_MSNLEN); | ||
1714 | memcpy(m->plmsn, m->lmsn, ISDN_LMSNLEN); | ||
1715 | if (dev->profd) | ||
1716 | send_sig(SIGIO, dev->profd, 1); | ||
1717 | } | ||
1718 | |||
1719 | static const struct tty_operations modem_ops = { | ||
1720 | .install = isdn_tty_install, | ||
1721 | .open = isdn_tty_open, | ||
1722 | .close = isdn_tty_close, | ||
1723 | .write = isdn_tty_write, | ||
1724 | .flush_chars = isdn_tty_flush_chars, | ||
1725 | .write_room = isdn_tty_write_room, | ||
1726 | .chars_in_buffer = isdn_tty_chars_in_buffer, | ||
1727 | .flush_buffer = isdn_tty_flush_buffer, | ||
1728 | .ioctl = isdn_tty_ioctl, | ||
1729 | .throttle = isdn_tty_throttle, | ||
1730 | .unthrottle = isdn_tty_unthrottle, | ||
1731 | .set_termios = isdn_tty_set_termios, | ||
1732 | .hangup = isdn_tty_hangup, | ||
1733 | .tiocmget = isdn_tty_tiocmget, | ||
1734 | .tiocmset = isdn_tty_tiocmset, | ||
1735 | }; | ||
1736 | |||
1737 | static int isdn_tty_carrier_raised(struct tty_port *port) | ||
1738 | { | ||
1739 | modem_info *info = container_of(port, modem_info, port); | ||
1740 | return info->msr & UART_MSR_DCD; | ||
1741 | } | ||
1742 | |||
1743 | static const struct tty_port_operations isdn_tty_port_ops = { | ||
1744 | .carrier_raised = isdn_tty_carrier_raised, | ||
1745 | }; | ||
1746 | |||
1747 | int | ||
1748 | isdn_tty_modem_init(void) | ||
1749 | { | ||
1750 | isdn_modem_t *m; | ||
1751 | int i, retval; | ||
1752 | modem_info *info; | ||
1753 | |||
1754 | m = &dev->mdm; | ||
1755 | m->tty_modem = alloc_tty_driver(ISDN_MAX_CHANNELS); | ||
1756 | if (!m->tty_modem) | ||
1757 | return -ENOMEM; | ||
1758 | m->tty_modem->name = "ttyI"; | ||
1759 | m->tty_modem->major = ISDN_TTY_MAJOR; | ||
1760 | m->tty_modem->minor_start = 0; | ||
1761 | m->tty_modem->type = TTY_DRIVER_TYPE_SERIAL; | ||
1762 | m->tty_modem->subtype = SERIAL_TYPE_NORMAL; | ||
1763 | m->tty_modem->init_termios = tty_std_termios; | ||
1764 | m->tty_modem->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; | ||
1765 | m->tty_modem->flags = TTY_DRIVER_REAL_RAW; | ||
1766 | m->tty_modem->driver_name = "isdn_tty"; | ||
1767 | tty_set_operations(m->tty_modem, &modem_ops); | ||
1768 | retval = tty_register_driver(m->tty_modem); | ||
1769 | if (retval) { | ||
1770 | printk(KERN_WARNING "isdn_tty: Couldn't register modem-device\n"); | ||
1771 | goto err; | ||
1772 | } | ||
1773 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
1774 | info = &m->info[i]; | ||
1775 | #ifdef CONFIG_ISDN_TTY_FAX | ||
1776 | if (!(info->fax = kmalloc(sizeof(T30_s), GFP_KERNEL))) { | ||
1777 | printk(KERN_ERR "Could not allocate fax t30-buffer\n"); | ||
1778 | retval = -ENOMEM; | ||
1779 | goto err_unregister; | ||
1780 | } | ||
1781 | #endif | ||
1782 | tty_port_init(&info->port); | ||
1783 | info->port.ops = &isdn_tty_port_ops; | ||
1784 | spin_lock_init(&info->readlock); | ||
1785 | sprintf(info->last_cause, "0000"); | ||
1786 | sprintf(info->last_num, "none"); | ||
1787 | info->last_dir = 0; | ||
1788 | info->last_lhup = 1; | ||
1789 | info->last_l2 = -1; | ||
1790 | info->last_si = 0; | ||
1791 | isdn_tty_reset_profile(&info->emu); | ||
1792 | isdn_tty_modem_reset_regs(info, 1); | ||
1793 | info->magic = ISDN_ASYNC_MAGIC; | ||
1794 | info->line = i; | ||
1795 | info->x_char = 0; | ||
1796 | info->isdn_driver = -1; | ||
1797 | info->isdn_channel = -1; | ||
1798 | info->drv_index = -1; | ||
1799 | info->xmit_size = ISDN_SERIAL_XMIT_SIZE; | ||
1800 | timer_setup(&info->nc_timer, isdn_tty_modem_do_ncarrier, 0); | ||
1801 | skb_queue_head_init(&info->xmit_queue); | ||
1802 | #ifdef CONFIG_ISDN_AUDIO | ||
1803 | skb_queue_head_init(&info->dtmf_queue); | ||
1804 | #endif | ||
1805 | info->port.xmit_buf = kmalloc(ISDN_SERIAL_XMIT_MAX + 5, | ||
1806 | GFP_KERNEL); | ||
1807 | if (!info->port.xmit_buf) { | ||
1808 | printk(KERN_ERR "Could not allocate modem xmit-buffer\n"); | ||
1809 | retval = -ENOMEM; | ||
1810 | goto err_unregister; | ||
1811 | } | ||
1812 | /* Make room for T.70 header */ | ||
1813 | info->port.xmit_buf += 4; | ||
1814 | } | ||
1815 | return 0; | ||
1816 | err_unregister: | ||
1817 | for (i--; i >= 0; i--) { | ||
1818 | info = &m->info[i]; | ||
1819 | #ifdef CONFIG_ISDN_TTY_FAX | ||
1820 | kfree(info->fax); | ||
1821 | #endif | ||
1822 | kfree(info->port.xmit_buf - 4); | ||
1823 | info->port.xmit_buf = NULL; | ||
1824 | tty_port_destroy(&info->port); | ||
1825 | } | ||
1826 | tty_unregister_driver(m->tty_modem); | ||
1827 | err: | ||
1828 | put_tty_driver(m->tty_modem); | ||
1829 | m->tty_modem = NULL; | ||
1830 | return retval; | ||
1831 | } | ||
1832 | |||
1833 | void | ||
1834 | isdn_tty_exit(void) | ||
1835 | { | ||
1836 | modem_info *info; | ||
1837 | int i; | ||
1838 | |||
1839 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
1840 | info = &dev->mdm.info[i]; | ||
1841 | isdn_tty_cleanup_xmit(info); | ||
1842 | #ifdef CONFIG_ISDN_TTY_FAX | ||
1843 | kfree(info->fax); | ||
1844 | #endif | ||
1845 | kfree(info->port.xmit_buf - 4); | ||
1846 | info->port.xmit_buf = NULL; | ||
1847 | tty_port_destroy(&info->port); | ||
1848 | } | ||
1849 | tty_unregister_driver(dev->mdm.tty_modem); | ||
1850 | put_tty_driver(dev->mdm.tty_modem); | ||
1851 | dev->mdm.tty_modem = NULL; | ||
1852 | } | ||
1853 | |||
1854 | |||
1855 | /* | ||
1856 | * isdn_tty_match_icall(char *MSN, atemu *tty_emulator, int dev_idx) | ||
1857 | * match the MSN against the MSNs (glob patterns) defined for tty_emulator, | ||
1858 | * and return 0 for match, 1 for no match, 2 if MSN could match if longer. | ||
1859 | */ | ||
1860 | |||
1861 | static int | ||
1862 | isdn_tty_match_icall(char *cid, atemu *emu, int di) | ||
1863 | { | ||
1864 | #ifdef ISDN_DEBUG_MODEM_ICALL | ||
1865 | printk(KERN_DEBUG "m_fi: msn=%s lmsn=%s mmsn=%s mreg[SI1]=%d mreg[SI2]=%d\n", | ||
1866 | emu->msn, emu->lmsn, isdn_map_eaz2msn(emu->msn, di), | ||
1867 | emu->mdmreg[REG_SI1], emu->mdmreg[REG_SI2]); | ||
1868 | #endif | ||
1869 | if (strlen(emu->lmsn)) { | ||
1870 | char *p = emu->lmsn; | ||
1871 | char *q; | ||
1872 | int tmp; | ||
1873 | int ret = 0; | ||
1874 | |||
1875 | while (1) { | ||
1876 | if ((q = strchr(p, ';'))) | ||
1877 | *q = '\0'; | ||
1878 | if ((tmp = isdn_msncmp(cid, isdn_map_eaz2msn(p, di))) > ret) | ||
1879 | ret = tmp; | ||
1880 | #ifdef ISDN_DEBUG_MODEM_ICALL | ||
1881 | printk(KERN_DEBUG "m_fi: lmsnX=%s mmsn=%s -> tmp=%d\n", | ||
1882 | p, isdn_map_eaz2msn(emu->msn, di), tmp); | ||
1883 | #endif | ||
1884 | if (q) { | ||
1885 | *q = ';'; | ||
1886 | p = q; | ||
1887 | p++; | ||
1888 | } | ||
1889 | if (!tmp) | ||
1890 | return 0; | ||
1891 | if (!q) | ||
1892 | break; | ||
1893 | } | ||
1894 | return ret; | ||
1895 | } else { | ||
1896 | int tmp; | ||
1897 | tmp = isdn_msncmp(cid, isdn_map_eaz2msn(emu->msn, di)); | ||
1898 | #ifdef ISDN_DEBUG_MODEM_ICALL | ||
1899 | printk(KERN_DEBUG "m_fi: mmsn=%s -> tmp=%d\n", | ||
1900 | isdn_map_eaz2msn(emu->msn, di), tmp); | ||
1901 | #endif | ||
1902 | return tmp; | ||
1903 | } | ||
1904 | } | ||
1905 | |||
1906 | /* | ||
1907 | * An incoming call-request has arrived. | ||
1908 | * Search the tty-devices for an appropriate device and bind | ||
1909 | * it to the ISDN-Channel. | ||
1910 | * Return: | ||
1911 | * | ||
1912 | * 0 = No matching device found. | ||
1913 | * 1 = A matching device found. | ||
1914 | * 3 = No match found, but eventually would match, if | ||
1915 | * CID is longer. | ||
1916 | */ | ||
1917 | int | ||
1918 | isdn_tty_find_icall(int di, int ch, setup_parm *setup) | ||
1919 | { | ||
1920 | char *eaz; | ||
1921 | int i; | ||
1922 | int wret; | ||
1923 | int idx; | ||
1924 | int si1; | ||
1925 | int si2; | ||
1926 | char *nr; | ||
1927 | ulong flags; | ||
1928 | |||
1929 | if (!setup->phone[0]) { | ||
1930 | nr = "0"; | ||
1931 | printk(KERN_INFO "isdn_tty: Incoming call without OAD, assuming '0'\n"); | ||
1932 | } else | ||
1933 | nr = setup->phone; | ||
1934 | si1 = (int) setup->si1; | ||
1935 | si2 = (int) setup->si2; | ||
1936 | if (!setup->eazmsn[0]) { | ||
1937 | printk(KERN_WARNING "isdn_tty: Incoming call without CPN, assuming '0'\n"); | ||
1938 | eaz = "0"; | ||
1939 | } else | ||
1940 | eaz = setup->eazmsn; | ||
1941 | #ifdef ISDN_DEBUG_MODEM_ICALL | ||
1942 | printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2); | ||
1943 | #endif | ||
1944 | wret = 0; | ||
1945 | spin_lock_irqsave(&dev->lock, flags); | ||
1946 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
1947 | modem_info *info = &dev->mdm.info[i]; | ||
1948 | |||
1949 | if (info->port.count == 0) | ||
1950 | continue; | ||
1951 | if ((info->emu.mdmreg[REG_SI1] & si2bit[si1]) && /* SI1 is matching */ | ||
1952 | (info->emu.mdmreg[REG_SI2] == si2)) { /* SI2 is matching */ | ||
1953 | idx = isdn_dc2minor(di, ch); | ||
1954 | #ifdef ISDN_DEBUG_MODEM_ICALL | ||
1955 | printk(KERN_DEBUG "m_fi: match1 wret=%d\n", wret); | ||
1956 | printk(KERN_DEBUG "m_fi: idx=%d flags=%08lx drv=%d ch=%d usg=%d\n", idx, | ||
1957 | info->port.flags, info->isdn_driver, | ||
1958 | info->isdn_channel, dev->usage[idx]); | ||
1959 | #endif | ||
1960 | if ( | ||
1961 | #ifndef FIX_FILE_TRANSFER | ||
1962 | tty_port_active(&info->port) && | ||
1963 | #endif | ||
1964 | (info->isdn_driver == -1) && | ||
1965 | (info->isdn_channel == -1) && | ||
1966 | (USG_NONE(dev->usage[idx]))) { | ||
1967 | int matchret; | ||
1968 | |||
1969 | if ((matchret = isdn_tty_match_icall(eaz, &info->emu, di)) > wret) | ||
1970 | wret = matchret; | ||
1971 | if (!matchret) { /* EAZ is matching */ | ||
1972 | info->isdn_driver = di; | ||
1973 | info->isdn_channel = ch; | ||
1974 | info->drv_index = idx; | ||
1975 | dev->m_idx[idx] = info->line; | ||
1976 | dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE; | ||
1977 | dev->usage[idx] |= isdn_calc_usage(si1, info->emu.mdmreg[REG_L2PROT]); | ||
1978 | strcpy(dev->num[idx], nr); | ||
1979 | strcpy(info->emu.cpn, eaz); | ||
1980 | info->emu.mdmreg[REG_SI1I] = si2bit[si1]; | ||
1981 | info->emu.mdmreg[REG_PLAN] = setup->plan; | ||
1982 | info->emu.mdmreg[REG_SCREEN] = setup->screen; | ||
1983 | isdn_info_update(); | ||
1984 | spin_unlock_irqrestore(&dev->lock, flags); | ||
1985 | printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr, | ||
1986 | info->line); | ||
1987 | info->msr |= UART_MSR_RI; | ||
1988 | isdn_tty_modem_result(RESULT_RING, info); | ||
1989 | isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1); | ||
1990 | return 1; | ||
1991 | } | ||
1992 | } | ||
1993 | } | ||
1994 | } | ||
1995 | spin_unlock_irqrestore(&dev->lock, flags); | ||
1996 | printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz, | ||
1997 | ((dev->drv[di]->flags & DRV_FLAG_REJBUS) && (wret != 2)) ? "rejected" : "ignored"); | ||
1998 | return (wret == 2) ? 3 : 0; | ||
1999 | } | ||
2000 | |||
2001 | int | ||
2002 | isdn_tty_stat_callback(int i, isdn_ctrl *c) | ||
2003 | { | ||
2004 | int mi; | ||
2005 | modem_info *info; | ||
2006 | char *e; | ||
2007 | |||
2008 | if (i < 0) | ||
2009 | return 0; | ||
2010 | if ((mi = dev->m_idx[i]) >= 0) { | ||
2011 | info = &dev->mdm.info[mi]; | ||
2012 | switch (c->command) { | ||
2013 | case ISDN_STAT_CINF: | ||
2014 | printk(KERN_DEBUG "CHARGEINFO on ttyI%d: %ld %s\n", info->line, c->arg, c->parm.num); | ||
2015 | info->emu.charge = (unsigned) simple_strtoul(c->parm.num, &e, 10); | ||
2016 | if (e == (char *)c->parm.num) | ||
2017 | info->emu.charge = 0; | ||
2018 | |||
2019 | break; | ||
2020 | case ISDN_STAT_BSENT: | ||
2021 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2022 | printk(KERN_DEBUG "tty_STAT_BSENT ttyI%d\n", info->line); | ||
2023 | #endif | ||
2024 | if ((info->isdn_driver == c->driver) && | ||
2025 | (info->isdn_channel == c->arg)) { | ||
2026 | info->msr |= UART_MSR_CTS; | ||
2027 | if (info->send_outstanding) | ||
2028 | if (!(--info->send_outstanding)) | ||
2029 | info->lsr |= UART_LSR_TEMT; | ||
2030 | isdn_tty_tint(info); | ||
2031 | return 1; | ||
2032 | } | ||
2033 | break; | ||
2034 | case ISDN_STAT_CAUSE: | ||
2035 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2036 | printk(KERN_DEBUG "tty_STAT_CAUSE ttyI%d\n", info->line); | ||
2037 | #endif | ||
2038 | /* Signal cause to tty-device */ | ||
2039 | strncpy(info->last_cause, c->parm.num, 5); | ||
2040 | return 1; | ||
2041 | case ISDN_STAT_DISPLAY: | ||
2042 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2043 | printk(KERN_DEBUG "tty_STAT_DISPLAY ttyI%d\n", info->line); | ||
2044 | #endif | ||
2045 | /* Signal display to tty-device */ | ||
2046 | if ((info->emu.mdmreg[REG_DISPLAY] & BIT_DISPLAY) && | ||
2047 | !(info->emu.mdmreg[REG_RESPNUM] & BIT_RESPNUM)) { | ||
2048 | isdn_tty_at_cout("\r\n", info); | ||
2049 | isdn_tty_at_cout("DISPLAY: ", info); | ||
2050 | isdn_tty_at_cout(c->parm.display, info); | ||
2051 | isdn_tty_at_cout("\r\n", info); | ||
2052 | } | ||
2053 | return 1; | ||
2054 | case ISDN_STAT_DCONN: | ||
2055 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2056 | printk(KERN_DEBUG "tty_STAT_DCONN ttyI%d\n", info->line); | ||
2057 | #endif | ||
2058 | if (tty_port_active(&info->port)) { | ||
2059 | if (info->dialing == 1) { | ||
2060 | info->dialing = 2; | ||
2061 | return 1; | ||
2062 | } | ||
2063 | } | ||
2064 | break; | ||
2065 | case ISDN_STAT_DHUP: | ||
2066 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2067 | printk(KERN_DEBUG "tty_STAT_DHUP ttyI%d\n", info->line); | ||
2068 | #endif | ||
2069 | if (tty_port_active(&info->port)) { | ||
2070 | if (info->dialing == 1) | ||
2071 | isdn_tty_modem_result(RESULT_BUSY, info); | ||
2072 | if (info->dialing > 1) | ||
2073 | isdn_tty_modem_result(RESULT_NO_CARRIER, info); | ||
2074 | info->dialing = 0; | ||
2075 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
2076 | printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n"); | ||
2077 | #endif | ||
2078 | isdn_tty_modem_hup(info, 0); | ||
2079 | return 1; | ||
2080 | } | ||
2081 | break; | ||
2082 | case ISDN_STAT_BCONN: | ||
2083 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2084 | printk(KERN_DEBUG "tty_STAT_BCONN ttyI%d\n", info->line); | ||
2085 | #endif | ||
2086 | /* Wake up any processes waiting | ||
2087 | * for incoming call of this device when | ||
2088 | * DCD follow the state of incoming carrier | ||
2089 | */ | ||
2090 | if (info->port.blocked_open && | ||
2091 | (info->emu.mdmreg[REG_DCD] & BIT_DCD)) { | ||
2092 | wake_up_interruptible(&info->port.open_wait); | ||
2093 | } | ||
2094 | |||
2095 | /* Schedule CONNECT-Message to any tty | ||
2096 | * waiting for it and | ||
2097 | * set DCD-bit of its modem-status. | ||
2098 | */ | ||
2099 | if (tty_port_active(&info->port) || | ||
2100 | (info->port.blocked_open && | ||
2101 | (info->emu.mdmreg[REG_DCD] & BIT_DCD))) { | ||
2102 | info->msr |= UART_MSR_DCD; | ||
2103 | info->emu.charge = 0; | ||
2104 | if (info->dialing & 0xf) | ||
2105 | info->last_dir = 1; | ||
2106 | else | ||
2107 | info->last_dir = 0; | ||
2108 | info->dialing = 0; | ||
2109 | info->rcvsched = 1; | ||
2110 | if (USG_MODEM(dev->usage[i])) { | ||
2111 | if (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) { | ||
2112 | strcpy(info->emu.connmsg, c->parm.num); | ||
2113 | isdn_tty_modem_result(RESULT_CONNECT, info); | ||
2114 | } else | ||
2115 | isdn_tty_modem_result(RESULT_CONNECT64000, info); | ||
2116 | } | ||
2117 | if (USG_VOICE(dev->usage[i])) | ||
2118 | isdn_tty_modem_result(RESULT_VCON, info); | ||
2119 | return 1; | ||
2120 | } | ||
2121 | break; | ||
2122 | case ISDN_STAT_BHUP: | ||
2123 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2124 | printk(KERN_DEBUG "tty_STAT_BHUP ttyI%d\n", info->line); | ||
2125 | #endif | ||
2126 | if (tty_port_active(&info->port)) { | ||
2127 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
2128 | printk(KERN_DEBUG "Mhup in ISDN_STAT_BHUP\n"); | ||
2129 | #endif | ||
2130 | isdn_tty_modem_hup(info, 0); | ||
2131 | return 1; | ||
2132 | } | ||
2133 | break; | ||
2134 | case ISDN_STAT_NODCH: | ||
2135 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2136 | printk(KERN_DEBUG "tty_STAT_NODCH ttyI%d\n", info->line); | ||
2137 | #endif | ||
2138 | if (tty_port_active(&info->port)) { | ||
2139 | if (info->dialing) { | ||
2140 | info->dialing = 0; | ||
2141 | info->last_l2 = -1; | ||
2142 | info->last_si = 0; | ||
2143 | sprintf(info->last_cause, "0000"); | ||
2144 | isdn_tty_modem_result(RESULT_NO_DIALTONE, info); | ||
2145 | } | ||
2146 | isdn_tty_modem_hup(info, 0); | ||
2147 | return 1; | ||
2148 | } | ||
2149 | break; | ||
2150 | case ISDN_STAT_UNLOAD: | ||
2151 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2152 | printk(KERN_DEBUG "tty_STAT_UNLOAD ttyI%d\n", info->line); | ||
2153 | #endif | ||
2154 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
2155 | info = &dev->mdm.info[i]; | ||
2156 | if (info->isdn_driver == c->driver) { | ||
2157 | if (info->online) | ||
2158 | isdn_tty_modem_hup(info, 1); | ||
2159 | } | ||
2160 | } | ||
2161 | return 1; | ||
2162 | #ifdef CONFIG_ISDN_TTY_FAX | ||
2163 | case ISDN_STAT_FAXIND: | ||
2164 | if (tty_port_active(&info->port)) { | ||
2165 | isdn_tty_fax_command(info, c); | ||
2166 | } | ||
2167 | break; | ||
2168 | #endif | ||
2169 | #ifdef CONFIG_ISDN_AUDIO | ||
2170 | case ISDN_STAT_AUDIO: | ||
2171 | if (tty_port_active(&info->port)) { | ||
2172 | switch (c->parm.num[0]) { | ||
2173 | case ISDN_AUDIO_DTMF: | ||
2174 | if (info->vonline) { | ||
2175 | isdn_audio_put_dle_code(info, | ||
2176 | c->parm.num[1]); | ||
2177 | } | ||
2178 | break; | ||
2179 | } | ||
2180 | } | ||
2181 | break; | ||
2182 | #endif | ||
2183 | } | ||
2184 | } | ||
2185 | return 0; | ||
2186 | } | ||
2187 | |||
2188 | /********************************************************************* | ||
2189 | Modem-Emulator-Routines | ||
2190 | *********************************************************************/ | ||
2191 | |||
2192 | #define cmdchar(c) ((c >= ' ') && (c <= 0x7f)) | ||
2193 | |||
2194 | /* | ||
2195 | * Put a message from the AT-emulator into receive-buffer of tty, | ||
2196 | * convert CR, LF, and BS to values in modem-registers 3, 4 and 5. | ||
2197 | */ | ||
2198 | void | ||
2199 | isdn_tty_at_cout(char *msg, modem_info *info) | ||
2200 | { | ||
2201 | struct tty_port *port = &info->port; | ||
2202 | atemu *m = &info->emu; | ||
2203 | char *p; | ||
2204 | char c; | ||
2205 | u_long flags; | ||
2206 | struct sk_buff *skb = NULL; | ||
2207 | char *sp = NULL; | ||
2208 | int l; | ||
2209 | |||
2210 | if (!msg) { | ||
2211 | printk(KERN_WARNING "isdn_tty: Null-Message in isdn_tty_at_cout\n"); | ||
2212 | return; | ||
2213 | } | ||
2214 | |||
2215 | l = strlen(msg); | ||
2216 | |||
2217 | spin_lock_irqsave(&info->readlock, flags); | ||
2218 | if (info->closing) { | ||
2219 | spin_unlock_irqrestore(&info->readlock, flags); | ||
2220 | return; | ||
2221 | } | ||
2222 | |||
2223 | /* use queue instead of direct, if online and */ | ||
2224 | /* data is in queue or buffer is full */ | ||
2225 | if (info->online && ((tty_buffer_request_room(port, l) < l) || | ||
2226 | !skb_queue_empty(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel]))) { | ||
2227 | skb = alloc_skb(l, GFP_ATOMIC); | ||
2228 | if (!skb) { | ||
2229 | spin_unlock_irqrestore(&info->readlock, flags); | ||
2230 | return; | ||
2231 | } | ||
2232 | sp = skb_put(skb, l); | ||
2233 | #ifdef CONFIG_ISDN_AUDIO | ||
2234 | ISDN_AUDIO_SKB_DLECOUNT(skb) = 0; | ||
2235 | ISDN_AUDIO_SKB_LOCK(skb) = 0; | ||
2236 | #endif | ||
2237 | } | ||
2238 | |||
2239 | for (p = msg; *p; p++) { | ||
2240 | switch (*p) { | ||
2241 | case '\r': | ||
2242 | c = m->mdmreg[REG_CR]; | ||
2243 | break; | ||
2244 | case '\n': | ||
2245 | c = m->mdmreg[REG_LF]; | ||
2246 | break; | ||
2247 | case '\b': | ||
2248 | c = m->mdmreg[REG_BS]; | ||
2249 | break; | ||
2250 | default: | ||
2251 | c = *p; | ||
2252 | } | ||
2253 | if (skb) { | ||
2254 | *sp++ = c; | ||
2255 | } else { | ||
2256 | if (tty_insert_flip_char(port, c, TTY_NORMAL) == 0) | ||
2257 | break; | ||
2258 | } | ||
2259 | } | ||
2260 | if (skb) { | ||
2261 | __skb_queue_tail(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel], skb); | ||
2262 | dev->drv[info->isdn_driver]->rcvcount[info->isdn_channel] += skb->len; | ||
2263 | spin_unlock_irqrestore(&info->readlock, flags); | ||
2264 | /* Schedule dequeuing */ | ||
2265 | if (dev->modempoll && info->rcvsched) | ||
2266 | isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); | ||
2267 | |||
2268 | } else { | ||
2269 | spin_unlock_irqrestore(&info->readlock, flags); | ||
2270 | tty_flip_buffer_push(port); | ||
2271 | } | ||
2272 | } | ||
2273 | |||
2274 | /* | ||
2275 | * Perform ATH Hangup | ||
2276 | */ | ||
2277 | static void | ||
2278 | isdn_tty_on_hook(modem_info *info) | ||
2279 | { | ||
2280 | if (info->isdn_channel >= 0) { | ||
2281 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
2282 | printk(KERN_DEBUG "Mhup in isdn_tty_on_hook\n"); | ||
2283 | #endif | ||
2284 | isdn_tty_modem_hup(info, 1); | ||
2285 | } | ||
2286 | } | ||
2287 | |||
2288 | static void | ||
2289 | isdn_tty_off_hook(void) | ||
2290 | { | ||
2291 | printk(KERN_DEBUG "isdn_tty_off_hook\n"); | ||
2292 | } | ||
2293 | |||
2294 | #define PLUSWAIT1 (HZ / 2) /* 0.5 sec. */ | ||
2295 | #define PLUSWAIT2 (HZ * 3 / 2) /* 1.5 sec */ | ||
2296 | |||
2297 | /* | ||
2298 | * Check Buffer for Modem-escape-sequence, activate timer-callback to | ||
2299 | * isdn_tty_modem_escape() if sequence found. | ||
2300 | * | ||
2301 | * Parameters: | ||
2302 | * p pointer to databuffer | ||
2303 | * plus escape-character | ||
2304 | * count length of buffer | ||
2305 | * pluscount count of valid escape-characters so far | ||
2306 | * lastplus timestamp of last character | ||
2307 | */ | ||
2308 | static void | ||
2309 | isdn_tty_check_esc(const u_char *p, u_char plus, int count, int *pluscount, | ||
2310 | u_long *lastplus) | ||
2311 | { | ||
2312 | if (plus > 127) | ||
2313 | return; | ||
2314 | if (count > 3) { | ||
2315 | p += count - 3; | ||
2316 | count = 3; | ||
2317 | *pluscount = 0; | ||
2318 | } | ||
2319 | while (count > 0) { | ||
2320 | if (*(p++) == plus) { | ||
2321 | if ((*pluscount)++) { | ||
2322 | /* Time since last '+' > 0.5 sec. ? */ | ||
2323 | if (time_after(jiffies, *lastplus + PLUSWAIT1)) | ||
2324 | *pluscount = 1; | ||
2325 | } else { | ||
2326 | /* Time since last non-'+' < 1.5 sec. ? */ | ||
2327 | if (time_before(jiffies, *lastplus + PLUSWAIT2)) | ||
2328 | *pluscount = 0; | ||
2329 | } | ||
2330 | if ((*pluscount == 3) && (count == 1)) | ||
2331 | isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, 1); | ||
2332 | if (*pluscount > 3) | ||
2333 | *pluscount = 1; | ||
2334 | } else | ||
2335 | *pluscount = 0; | ||
2336 | *lastplus = jiffies; | ||
2337 | count--; | ||
2338 | } | ||
2339 | } | ||
2340 | |||
2341 | /* | ||
2342 | * Return result of AT-emulator to tty-receive-buffer, depending on | ||
2343 | * modem-register 12, bit 0 and 1. | ||
2344 | * For CONNECT-messages also switch to online-mode. | ||
2345 | * For RING-message handle auto-ATA if register 0 != 0 | ||
2346 | */ | ||
2347 | |||
2348 | static void | ||
2349 | isdn_tty_modem_result(int code, modem_info *info) | ||
2350 | { | ||
2351 | atemu *m = &info->emu; | ||
2352 | static char *msg[] = | ||
2353 | {"OK", "CONNECT", "RING", "NO CARRIER", "ERROR", | ||
2354 | "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER", | ||
2355 | "RINGING", "NO MSN/EAZ", "VCON", "RUNG"}; | ||
2356 | char s[ISDN_MSNLEN + 10]; | ||
2357 | |||
2358 | switch (code) { | ||
2359 | case RESULT_RING: | ||
2360 | m->mdmreg[REG_RINGCNT]++; | ||
2361 | if (m->mdmreg[REG_RINGCNT] == m->mdmreg[REG_RINGATA]) | ||
2362 | /* Automatically accept incoming call */ | ||
2363 | isdn_tty_cmd_ATA(info); | ||
2364 | break; | ||
2365 | case RESULT_NO_CARRIER: | ||
2366 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
2367 | printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n", | ||
2368 | info->closing, !info->port.tty); | ||
2369 | #endif | ||
2370 | m->mdmreg[REG_RINGCNT] = 0; | ||
2371 | del_timer(&info->nc_timer); | ||
2372 | info->ncarrier = 0; | ||
2373 | if (info->closing || !info->port.tty) | ||
2374 | return; | ||
2375 | |||
2376 | #ifdef CONFIG_ISDN_AUDIO | ||
2377 | if (info->vonline & 1) { | ||
2378 | #ifdef ISDN_DEBUG_MODEM_VOICE | ||
2379 | printk(KERN_DEBUG "res3: send DLE-ETX on ttyI%d\n", | ||
2380 | info->line); | ||
2381 | #endif | ||
2382 | /* voice-recording, add DLE-ETX */ | ||
2383 | isdn_tty_at_cout("\020\003", info); | ||
2384 | } | ||
2385 | if (info->vonline & 2) { | ||
2386 | #ifdef ISDN_DEBUG_MODEM_VOICE | ||
2387 | printk(KERN_DEBUG "res3: send DLE-DC4 on ttyI%d\n", | ||
2388 | info->line); | ||
2389 | #endif | ||
2390 | /* voice-playing, add DLE-DC4 */ | ||
2391 | isdn_tty_at_cout("\020\024", info); | ||
2392 | } | ||
2393 | #endif | ||
2394 | break; | ||
2395 | case RESULT_CONNECT: | ||
2396 | case RESULT_CONNECT64000: | ||
2397 | sprintf(info->last_cause, "0000"); | ||
2398 | if (!info->online) | ||
2399 | info->online = 2; | ||
2400 | break; | ||
2401 | case RESULT_VCON: | ||
2402 | #ifdef ISDN_DEBUG_MODEM_VOICE | ||
2403 | printk(KERN_DEBUG "res3: send VCON on ttyI%d\n", | ||
2404 | info->line); | ||
2405 | #endif | ||
2406 | sprintf(info->last_cause, "0000"); | ||
2407 | if (!info->online) | ||
2408 | info->online = 1; | ||
2409 | break; | ||
2410 | } /* switch (code) */ | ||
2411 | |||
2412 | if (m->mdmreg[REG_RESP] & BIT_RESP) { | ||
2413 | /* Show results */ | ||
2414 | if (m->mdmreg[REG_RESPNUM] & BIT_RESPNUM) { | ||
2415 | /* Show numeric results only */ | ||
2416 | sprintf(s, "\r\n%d\r\n", code); | ||
2417 | isdn_tty_at_cout(s, info); | ||
2418 | } else { | ||
2419 | if (code == RESULT_RING) { | ||
2420 | /* return if "show RUNG" and ringcounter>1 */ | ||
2421 | if ((m->mdmreg[REG_RUNG] & BIT_RUNG) && | ||
2422 | (m->mdmreg[REG_RINGCNT] > 1)) | ||
2423 | return; | ||
2424 | /* print CID, _before_ _every_ ring */ | ||
2425 | if (!(m->mdmreg[REG_CIDONCE] & BIT_CIDONCE)) { | ||
2426 | isdn_tty_at_cout("\r\nCALLER NUMBER: ", info); | ||
2427 | isdn_tty_at_cout(dev->num[info->drv_index], info); | ||
2428 | if (m->mdmreg[REG_CDN] & BIT_CDN) { | ||
2429 | isdn_tty_at_cout("\r\nCALLED NUMBER: ", info); | ||
2430 | isdn_tty_at_cout(info->emu.cpn, info); | ||
2431 | } | ||
2432 | } | ||
2433 | } | ||
2434 | isdn_tty_at_cout("\r\n", info); | ||
2435 | isdn_tty_at_cout(msg[code], info); | ||
2436 | switch (code) { | ||
2437 | case RESULT_CONNECT: | ||
2438 | switch (m->mdmreg[REG_L2PROT]) { | ||
2439 | case ISDN_PROTO_L2_MODEM: | ||
2440 | isdn_tty_at_cout(" ", info); | ||
2441 | isdn_tty_at_cout(m->connmsg, info); | ||
2442 | break; | ||
2443 | } | ||
2444 | break; | ||
2445 | case RESULT_RING: | ||
2446 | /* Append CPN, if enabled */ | ||
2447 | if ((m->mdmreg[REG_CPN] & BIT_CPN)) { | ||
2448 | sprintf(s, "/%s", m->cpn); | ||
2449 | isdn_tty_at_cout(s, info); | ||
2450 | } | ||
2451 | /* Print CID only once, _after_ 1st RING */ | ||
2452 | if ((m->mdmreg[REG_CIDONCE] & BIT_CIDONCE) && | ||
2453 | (m->mdmreg[REG_RINGCNT] == 1)) { | ||
2454 | isdn_tty_at_cout("\r\n", info); | ||
2455 | isdn_tty_at_cout("CALLER NUMBER: ", info); | ||
2456 | isdn_tty_at_cout(dev->num[info->drv_index], info); | ||
2457 | if (m->mdmreg[REG_CDN] & BIT_CDN) { | ||
2458 | isdn_tty_at_cout("\r\nCALLED NUMBER: ", info); | ||
2459 | isdn_tty_at_cout(info->emu.cpn, info); | ||
2460 | } | ||
2461 | } | ||
2462 | break; | ||
2463 | case RESULT_NO_CARRIER: | ||
2464 | case RESULT_NO_DIALTONE: | ||
2465 | case RESULT_BUSY: | ||
2466 | case RESULT_NO_ANSWER: | ||
2467 | m->mdmreg[REG_RINGCNT] = 0; | ||
2468 | /* Append Cause-Message if enabled */ | ||
2469 | if (m->mdmreg[REG_RESPXT] & BIT_RESPXT) { | ||
2470 | sprintf(s, "/%s", info->last_cause); | ||
2471 | isdn_tty_at_cout(s, info); | ||
2472 | } | ||
2473 | break; | ||
2474 | case RESULT_CONNECT64000: | ||
2475 | /* Append Protocol to CONNECT message */ | ||
2476 | switch (m->mdmreg[REG_L2PROT]) { | ||
2477 | case ISDN_PROTO_L2_X75I: | ||
2478 | case ISDN_PROTO_L2_X75UI: | ||
2479 | case ISDN_PROTO_L2_X75BUI: | ||
2480 | isdn_tty_at_cout("/X.75", info); | ||
2481 | break; | ||
2482 | case ISDN_PROTO_L2_HDLC: | ||
2483 | isdn_tty_at_cout("/HDLC", info); | ||
2484 | break; | ||
2485 | case ISDN_PROTO_L2_V11096: | ||
2486 | isdn_tty_at_cout("/V110/9600", info); | ||
2487 | break; | ||
2488 | case ISDN_PROTO_L2_V11019: | ||
2489 | isdn_tty_at_cout("/V110/19200", info); | ||
2490 | break; | ||
2491 | case ISDN_PROTO_L2_V11038: | ||
2492 | isdn_tty_at_cout("/V110/38400", info); | ||
2493 | break; | ||
2494 | } | ||
2495 | if (m->mdmreg[REG_T70] & BIT_T70) { | ||
2496 | isdn_tty_at_cout("/T.70", info); | ||
2497 | if (m->mdmreg[REG_T70] & BIT_T70_EXT) | ||
2498 | isdn_tty_at_cout("+", info); | ||
2499 | } | ||
2500 | break; | ||
2501 | } | ||
2502 | isdn_tty_at_cout("\r\n", info); | ||
2503 | } | ||
2504 | } | ||
2505 | if (code == RESULT_NO_CARRIER) { | ||
2506 | if (info->closing || (!info->port.tty)) | ||
2507 | return; | ||
2508 | |||
2509 | if (tty_port_check_carrier(&info->port)) | ||
2510 | tty_hangup(info->port.tty); | ||
2511 | } | ||
2512 | } | ||
2513 | |||
2514 | |||
2515 | /* | ||
2516 | * Display a modem-register-value. | ||
2517 | */ | ||
2518 | static void | ||
2519 | isdn_tty_show_profile(int ridx, modem_info *info) | ||
2520 | { | ||
2521 | char v[6]; | ||
2522 | |||
2523 | sprintf(v, "\r\n%d", info->emu.mdmreg[ridx]); | ||
2524 | isdn_tty_at_cout(v, info); | ||
2525 | } | ||
2526 | |||
2527 | /* | ||
2528 | * Get MSN-string from char-pointer, set pointer to end of number | ||
2529 | */ | ||
2530 | static void | ||
2531 | isdn_tty_get_msnstr(char *n, char **p) | ||
2532 | { | ||
2533 | int limit = ISDN_MSNLEN - 1; | ||
2534 | |||
2535 | while (((*p[0] >= '0' && *p[0] <= '9') || | ||
2536 | /* Why a comma ??? */ | ||
2537 | (*p[0] == ',') || (*p[0] == ':')) && | ||
2538 | (limit--)) | ||
2539 | *n++ = *p[0]++; | ||
2540 | *n = '\0'; | ||
2541 | } | ||
2542 | |||
2543 | /* | ||
2544 | * Get phone-number from modem-commandbuffer | ||
2545 | */ | ||
2546 | static void | ||
2547 | isdn_tty_getdial(char *p, char *q, int cnt) | ||
2548 | { | ||
2549 | int first = 1; | ||
2550 | int limit = ISDN_MSNLEN - 1; /* MUST match the size of interface var to avoid | ||
2551 | buffer overflow */ | ||
2552 | |||
2553 | while (strchr(" 0123456789,#.*WPTSR-", *p) && *p && --cnt > 0) { | ||
2554 | if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first) || | ||
2555 | ((*p == 'R') && first) || | ||
2556 | (*p == '*') || (*p == '#')) { | ||
2557 | *q++ = *p; | ||
2558 | limit--; | ||
2559 | } | ||
2560 | if (!limit) | ||
2561 | break; | ||
2562 | p++; | ||
2563 | first = 0; | ||
2564 | } | ||
2565 | *q = 0; | ||
2566 | } | ||
2567 | |||
2568 | #define PARSE_ERROR { isdn_tty_modem_result(RESULT_ERROR, info); return; } | ||
2569 | #define PARSE_ERROR1 { isdn_tty_modem_result(RESULT_ERROR, info); return 1; } | ||
2570 | |||
2571 | static void | ||
2572 | isdn_tty_report(modem_info *info) | ||
2573 | { | ||
2574 | atemu *m = &info->emu; | ||
2575 | char s[80]; | ||
2576 | |||
2577 | isdn_tty_at_cout("\r\nStatistics of last connection:\r\n\r\n", info); | ||
2578 | sprintf(s, " Remote Number: %s\r\n", info->last_num); | ||
2579 | isdn_tty_at_cout(s, info); | ||
2580 | sprintf(s, " Direction: %s\r\n", info->last_dir ? "outgoing" : "incoming"); | ||
2581 | isdn_tty_at_cout(s, info); | ||
2582 | isdn_tty_at_cout(" Layer-2 Protocol: ", info); | ||
2583 | switch (info->last_l2) { | ||
2584 | case ISDN_PROTO_L2_X75I: | ||
2585 | isdn_tty_at_cout("X.75i", info); | ||
2586 | break; | ||
2587 | case ISDN_PROTO_L2_X75UI: | ||
2588 | isdn_tty_at_cout("X.75ui", info); | ||
2589 | break; | ||
2590 | case ISDN_PROTO_L2_X75BUI: | ||
2591 | isdn_tty_at_cout("X.75bui", info); | ||
2592 | break; | ||
2593 | case ISDN_PROTO_L2_HDLC: | ||
2594 | isdn_tty_at_cout("HDLC", info); | ||
2595 | break; | ||
2596 | case ISDN_PROTO_L2_V11096: | ||
2597 | isdn_tty_at_cout("V.110 9600 Baud", info); | ||
2598 | break; | ||
2599 | case ISDN_PROTO_L2_V11019: | ||
2600 | isdn_tty_at_cout("V.110 19200 Baud", info); | ||
2601 | break; | ||
2602 | case ISDN_PROTO_L2_V11038: | ||
2603 | isdn_tty_at_cout("V.110 38400 Baud", info); | ||
2604 | break; | ||
2605 | case ISDN_PROTO_L2_TRANS: | ||
2606 | isdn_tty_at_cout("transparent", info); | ||
2607 | break; | ||
2608 | case ISDN_PROTO_L2_MODEM: | ||
2609 | isdn_tty_at_cout("modem", info); | ||
2610 | break; | ||
2611 | case ISDN_PROTO_L2_FAX: | ||
2612 | isdn_tty_at_cout("fax", info); | ||
2613 | break; | ||
2614 | default: | ||
2615 | isdn_tty_at_cout("unknown", info); | ||
2616 | break; | ||
2617 | } | ||
2618 | if (m->mdmreg[REG_T70] & BIT_T70) { | ||
2619 | isdn_tty_at_cout("/T.70", info); | ||
2620 | if (m->mdmreg[REG_T70] & BIT_T70_EXT) | ||
2621 | isdn_tty_at_cout("+", info); | ||
2622 | } | ||
2623 | isdn_tty_at_cout("\r\n", info); | ||
2624 | isdn_tty_at_cout(" Service: ", info); | ||
2625 | switch (info->last_si) { | ||
2626 | case 1: | ||
2627 | isdn_tty_at_cout("audio\r\n", info); | ||
2628 | break; | ||
2629 | case 5: | ||
2630 | isdn_tty_at_cout("btx\r\n", info); | ||
2631 | break; | ||
2632 | case 7: | ||
2633 | isdn_tty_at_cout("data\r\n", info); | ||
2634 | break; | ||
2635 | default: | ||
2636 | sprintf(s, "%d\r\n", info->last_si); | ||
2637 | isdn_tty_at_cout(s, info); | ||
2638 | break; | ||
2639 | } | ||
2640 | sprintf(s, " Hangup location: %s\r\n", info->last_lhup ? "local" : "remote"); | ||
2641 | isdn_tty_at_cout(s, info); | ||
2642 | sprintf(s, " Last cause: %s\r\n", info->last_cause); | ||
2643 | isdn_tty_at_cout(s, info); | ||
2644 | } | ||
2645 | |||
2646 | /* | ||
2647 | * Parse AT&.. commands. | ||
2648 | */ | ||
2649 | static int | ||
2650 | isdn_tty_cmd_ATand(char **p, modem_info *info) | ||
2651 | { | ||
2652 | atemu *m = &info->emu; | ||
2653 | int i; | ||
2654 | char rb[100]; | ||
2655 | |||
2656 | #define MAXRB (sizeof(rb) - 1) | ||
2657 | |||
2658 | switch (*p[0]) { | ||
2659 | case 'B': | ||
2660 | /* &B - Set Buffersize */ | ||
2661 | p[0]++; | ||
2662 | i = isdn_getnum(p); | ||
2663 | if ((i < 0) || (i > ISDN_SERIAL_XMIT_MAX)) | ||
2664 | PARSE_ERROR1; | ||
2665 | #ifdef CONFIG_ISDN_AUDIO | ||
2666 | if ((m->mdmreg[REG_SI1] & 1) && (i > VBUF)) | ||
2667 | PARSE_ERROR1; | ||
2668 | #endif | ||
2669 | m->mdmreg[REG_PSIZE] = i / 16; | ||
2670 | info->xmit_size = m->mdmreg[REG_PSIZE] * 16; | ||
2671 | switch (m->mdmreg[REG_L2PROT]) { | ||
2672 | case ISDN_PROTO_L2_V11096: | ||
2673 | case ISDN_PROTO_L2_V11019: | ||
2674 | case ISDN_PROTO_L2_V11038: | ||
2675 | info->xmit_size /= 10; | ||
2676 | } | ||
2677 | break; | ||
2678 | case 'C': | ||
2679 | /* &C - DCD Status */ | ||
2680 | p[0]++; | ||
2681 | switch (isdn_getnum(p)) { | ||
2682 | case 0: | ||
2683 | m->mdmreg[REG_DCD] &= ~BIT_DCD; | ||
2684 | break; | ||
2685 | case 1: | ||
2686 | m->mdmreg[REG_DCD] |= BIT_DCD; | ||
2687 | break; | ||
2688 | default: | ||
2689 | PARSE_ERROR1 | ||
2690 | } | ||
2691 | break; | ||
2692 | case 'D': | ||
2693 | /* &D - Set DTR-Low-behavior */ | ||
2694 | p[0]++; | ||
2695 | switch (isdn_getnum(p)) { | ||
2696 | case 0: | ||
2697 | m->mdmreg[REG_DTRHUP] &= ~BIT_DTRHUP; | ||
2698 | m->mdmreg[REG_DTRR] &= ~BIT_DTRR; | ||
2699 | break; | ||
2700 | case 2: | ||
2701 | m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP; | ||
2702 | m->mdmreg[REG_DTRR] &= ~BIT_DTRR; | ||
2703 | break; | ||
2704 | case 3: | ||
2705 | m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP; | ||
2706 | m->mdmreg[REG_DTRR] |= BIT_DTRR; | ||
2707 | break; | ||
2708 | default: | ||
2709 | PARSE_ERROR1 | ||
2710 | } | ||
2711 | break; | ||
2712 | case 'E': | ||
2713 | /* &E -Set EAZ/MSN */ | ||
2714 | p[0]++; | ||
2715 | isdn_tty_get_msnstr(m->msn, p); | ||
2716 | break; | ||
2717 | case 'F': | ||
2718 | /* &F -Set Factory-Defaults */ | ||
2719 | p[0]++; | ||
2720 | if (info->msr & UART_MSR_DCD) | ||
2721 | PARSE_ERROR1; | ||
2722 | isdn_tty_reset_profile(m); | ||
2723 | isdn_tty_modem_reset_regs(info, 1); | ||
2724 | break; | ||
2725 | #ifdef DUMMY_HAYES_AT | ||
2726 | case 'K': | ||
2727 | /* only for be compilant with common scripts */ | ||
2728 | /* &K Flowcontrol - no function */ | ||
2729 | p[0]++; | ||
2730 | isdn_getnum(p); | ||
2731 | break; | ||
2732 | #endif | ||
2733 | case 'L': | ||
2734 | /* &L -Set Numbers to listen on */ | ||
2735 | p[0]++; | ||
2736 | i = 0; | ||
2737 | while (*p[0] && (strchr("0123456789,-*[]?;", *p[0])) && | ||
2738 | (i < ISDN_LMSNLEN - 1)) | ||
2739 | m->lmsn[i++] = *p[0]++; | ||
2740 | m->lmsn[i] = '\0'; | ||
2741 | break; | ||
2742 | case 'R': | ||
2743 | /* &R - Set V.110 bitrate adaption */ | ||
2744 | p[0]++; | ||
2745 | i = isdn_getnum(p); | ||
2746 | switch (i) { | ||
2747 | case 0: | ||
2748 | /* Switch off V.110, back to X.75 */ | ||
2749 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; | ||
2750 | m->mdmreg[REG_SI2] = 0; | ||
2751 | info->xmit_size = m->mdmreg[REG_PSIZE] * 16; | ||
2752 | break; | ||
2753 | case 9600: | ||
2754 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11096; | ||
2755 | m->mdmreg[REG_SI2] = 197; | ||
2756 | info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10; | ||
2757 | break; | ||
2758 | case 19200: | ||
2759 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11019; | ||
2760 | m->mdmreg[REG_SI2] = 199; | ||
2761 | info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10; | ||
2762 | break; | ||
2763 | case 38400: | ||
2764 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11038; | ||
2765 | m->mdmreg[REG_SI2] = 198; /* no existing standard for this */ | ||
2766 | info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10; | ||
2767 | break; | ||
2768 | default: | ||
2769 | PARSE_ERROR1; | ||
2770 | } | ||
2771 | /* Switch off T.70 */ | ||
2772 | m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT); | ||
2773 | /* Set Service 7 */ | ||
2774 | m->mdmreg[REG_SI1] |= 4; | ||
2775 | break; | ||
2776 | case 'S': | ||
2777 | /* &S - Set Windowsize */ | ||
2778 | p[0]++; | ||
2779 | i = isdn_getnum(p); | ||
2780 | if ((i > 0) && (i < 9)) | ||
2781 | m->mdmreg[REG_WSIZE] = i; | ||
2782 | else | ||
2783 | PARSE_ERROR1; | ||
2784 | break; | ||
2785 | case 'V': | ||
2786 | /* &V - Show registers */ | ||
2787 | p[0]++; | ||
2788 | isdn_tty_at_cout("\r\n", info); | ||
2789 | for (i = 0; i < ISDN_MODEM_NUMREG; i++) { | ||
2790 | sprintf(rb, "S%02d=%03d%s", i, | ||
2791 | m->mdmreg[i], ((i + 1) % 10) ? " " : "\r\n"); | ||
2792 | isdn_tty_at_cout(rb, info); | ||
2793 | } | ||
2794 | sprintf(rb, "\r\nEAZ/MSN: %.50s\r\n", | ||
2795 | strlen(m->msn) ? m->msn : "None"); | ||
2796 | isdn_tty_at_cout(rb, info); | ||
2797 | if (strlen(m->lmsn)) { | ||
2798 | isdn_tty_at_cout("\r\nListen: ", info); | ||
2799 | isdn_tty_at_cout(m->lmsn, info); | ||
2800 | isdn_tty_at_cout("\r\n", info); | ||
2801 | } | ||
2802 | break; | ||
2803 | case 'W': | ||
2804 | /* &W - Write Profile */ | ||
2805 | p[0]++; | ||
2806 | switch (*p[0]) { | ||
2807 | case '0': | ||
2808 | p[0]++; | ||
2809 | modem_write_profile(m); | ||
2810 | break; | ||
2811 | default: | ||
2812 | PARSE_ERROR1; | ||
2813 | } | ||
2814 | break; | ||
2815 | case 'X': | ||
2816 | /* &X - Switch to BTX-Mode and T.70 */ | ||
2817 | p[0]++; | ||
2818 | switch (isdn_getnum(p)) { | ||
2819 | case 0: | ||
2820 | m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT); | ||
2821 | info->xmit_size = m->mdmreg[REG_PSIZE] * 16; | ||
2822 | break; | ||
2823 | case 1: | ||
2824 | m->mdmreg[REG_T70] |= BIT_T70; | ||
2825 | m->mdmreg[REG_T70] &= ~BIT_T70_EXT; | ||
2826 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; | ||
2827 | info->xmit_size = 112; | ||
2828 | m->mdmreg[REG_SI1] = 4; | ||
2829 | m->mdmreg[REG_SI2] = 0; | ||
2830 | break; | ||
2831 | case 2: | ||
2832 | m->mdmreg[REG_T70] |= (BIT_T70 | BIT_T70_EXT); | ||
2833 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; | ||
2834 | info->xmit_size = 112; | ||
2835 | m->mdmreg[REG_SI1] = 4; | ||
2836 | m->mdmreg[REG_SI2] = 0; | ||
2837 | break; | ||
2838 | default: | ||
2839 | PARSE_ERROR1; | ||
2840 | } | ||
2841 | break; | ||
2842 | default: | ||
2843 | PARSE_ERROR1; | ||
2844 | } | ||
2845 | return 0; | ||
2846 | } | ||
2847 | |||
2848 | static int | ||
2849 | isdn_tty_check_ats(int mreg, int mval, modem_info *info, atemu *m) | ||
2850 | { | ||
2851 | /* Some plausibility checks */ | ||
2852 | switch (mreg) { | ||
2853 | case REG_L2PROT: | ||
2854 | if (mval > ISDN_PROTO_L2_MAX) | ||
2855 | return 1; | ||
2856 | break; | ||
2857 | case REG_PSIZE: | ||
2858 | if ((mval * 16) > ISDN_SERIAL_XMIT_MAX) | ||
2859 | return 1; | ||
2860 | #ifdef CONFIG_ISDN_AUDIO | ||
2861 | if ((m->mdmreg[REG_SI1] & 1) && (mval > VBUFX)) | ||
2862 | return 1; | ||
2863 | #endif | ||
2864 | info->xmit_size = mval * 16; | ||
2865 | switch (m->mdmreg[REG_L2PROT]) { | ||
2866 | case ISDN_PROTO_L2_V11096: | ||
2867 | case ISDN_PROTO_L2_V11019: | ||
2868 | case ISDN_PROTO_L2_V11038: | ||
2869 | info->xmit_size /= 10; | ||
2870 | } | ||
2871 | break; | ||
2872 | case REG_SI1I: | ||
2873 | case REG_PLAN: | ||
2874 | case REG_SCREEN: | ||
2875 | /* readonly registers */ | ||
2876 | return 1; | ||
2877 | } | ||
2878 | return 0; | ||
2879 | } | ||
2880 | |||
2881 | /* | ||
2882 | * Perform ATS command | ||
2883 | */ | ||
2884 | static int | ||
2885 | isdn_tty_cmd_ATS(char **p, modem_info *info) | ||
2886 | { | ||
2887 | atemu *m = &info->emu; | ||
2888 | int bitpos; | ||
2889 | int mreg; | ||
2890 | int mval; | ||
2891 | int bval; | ||
2892 | |||
2893 | mreg = isdn_getnum(p); | ||
2894 | if (mreg < 0 || mreg >= ISDN_MODEM_NUMREG) | ||
2895 | PARSE_ERROR1; | ||
2896 | switch (*p[0]) { | ||
2897 | case '=': | ||
2898 | p[0]++; | ||
2899 | mval = isdn_getnum(p); | ||
2900 | if (mval < 0 || mval > 255) | ||
2901 | PARSE_ERROR1; | ||
2902 | if (isdn_tty_check_ats(mreg, mval, info, m)) | ||
2903 | PARSE_ERROR1; | ||
2904 | m->mdmreg[mreg] = mval; | ||
2905 | break; | ||
2906 | case '.': | ||
2907 | /* Set/Clear a single bit */ | ||
2908 | p[0]++; | ||
2909 | bitpos = isdn_getnum(p); | ||
2910 | if ((bitpos < 0) || (bitpos > 7)) | ||
2911 | PARSE_ERROR1; | ||
2912 | switch (*p[0]) { | ||
2913 | case '=': | ||
2914 | p[0]++; | ||
2915 | bval = isdn_getnum(p); | ||
2916 | if (bval < 0 || bval > 1) | ||
2917 | PARSE_ERROR1; | ||
2918 | if (bval) | ||
2919 | mval = m->mdmreg[mreg] | (1 << bitpos); | ||
2920 | else | ||
2921 | mval = m->mdmreg[mreg] & ~(1 << bitpos); | ||
2922 | if (isdn_tty_check_ats(mreg, mval, info, m)) | ||
2923 | PARSE_ERROR1; | ||
2924 | m->mdmreg[mreg] = mval; | ||
2925 | break; | ||
2926 | case '?': | ||
2927 | p[0]++; | ||
2928 | isdn_tty_at_cout("\r\n", info); | ||
2929 | isdn_tty_at_cout((m->mdmreg[mreg] & (1 << bitpos)) ? "1" : "0", | ||
2930 | info); | ||
2931 | break; | ||
2932 | default: | ||
2933 | PARSE_ERROR1; | ||
2934 | } | ||
2935 | break; | ||
2936 | case '?': | ||
2937 | p[0]++; | ||
2938 | isdn_tty_show_profile(mreg, info); | ||
2939 | break; | ||
2940 | default: | ||
2941 | PARSE_ERROR1; | ||
2942 | break; | ||
2943 | } | ||
2944 | return 0; | ||
2945 | } | ||
2946 | |||
2947 | /* | ||
2948 | * Perform ATA command | ||
2949 | */ | ||
2950 | static void | ||
2951 | isdn_tty_cmd_ATA(modem_info *info) | ||
2952 | { | ||
2953 | atemu *m = &info->emu; | ||
2954 | isdn_ctrl cmd; | ||
2955 | int l2; | ||
2956 | |||
2957 | if (info->msr & UART_MSR_RI) { | ||
2958 | /* Accept incoming call */ | ||
2959 | info->last_dir = 0; | ||
2960 | strcpy(info->last_num, dev->num[info->drv_index]); | ||
2961 | m->mdmreg[REG_RINGCNT] = 0; | ||
2962 | info->msr &= ~UART_MSR_RI; | ||
2963 | l2 = m->mdmreg[REG_L2PROT]; | ||
2964 | #ifdef CONFIG_ISDN_AUDIO | ||
2965 | /* If more than one bit set in reg18, autoselect Layer2 */ | ||
2966 | if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) { | ||
2967 | if (m->mdmreg[REG_SI1I] == 1) { | ||
2968 | if ((l2 != ISDN_PROTO_L2_MODEM) && (l2 != ISDN_PROTO_L2_FAX)) | ||
2969 | l2 = ISDN_PROTO_L2_TRANS; | ||
2970 | } else | ||
2971 | l2 = ISDN_PROTO_L2_X75I; | ||
2972 | } | ||
2973 | #endif | ||
2974 | cmd.driver = info->isdn_driver; | ||
2975 | cmd.command = ISDN_CMD_SETL2; | ||
2976 | cmd.arg = info->isdn_channel + (l2 << 8); | ||
2977 | info->last_l2 = l2; | ||
2978 | isdn_command(&cmd); | ||
2979 | cmd.driver = info->isdn_driver; | ||
2980 | cmd.command = ISDN_CMD_SETL3; | ||
2981 | cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); | ||
2982 | #ifdef CONFIG_ISDN_TTY_FAX | ||
2983 | if (l2 == ISDN_PROTO_L2_FAX) { | ||
2984 | cmd.parm.fax = info->fax; | ||
2985 | info->fax->direction = ISDN_TTY_FAX_CONN_IN; | ||
2986 | } | ||
2987 | #endif | ||
2988 | isdn_command(&cmd); | ||
2989 | cmd.driver = info->isdn_driver; | ||
2990 | cmd.arg = info->isdn_channel; | ||
2991 | cmd.command = ISDN_CMD_ACCEPTD; | ||
2992 | info->dialing = 16; | ||
2993 | info->emu.carrierwait = 0; | ||
2994 | isdn_command(&cmd); | ||
2995 | isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1); | ||
2996 | } else | ||
2997 | isdn_tty_modem_result(RESULT_NO_ANSWER, info); | ||
2998 | } | ||
2999 | |||
3000 | #ifdef CONFIG_ISDN_AUDIO | ||
3001 | /* | ||
3002 | * Parse AT+F.. commands | ||
3003 | */ | ||
3004 | static int | ||
3005 | isdn_tty_cmd_PLUSF(char **p, modem_info *info) | ||
3006 | { | ||
3007 | atemu *m = &info->emu; | ||
3008 | char rs[20]; | ||
3009 | |||
3010 | if (!strncmp(p[0], "CLASS", 5)) { | ||
3011 | p[0] += 5; | ||
3012 | switch (*p[0]) { | ||
3013 | case '?': | ||
3014 | p[0]++; | ||
3015 | sprintf(rs, "\r\n%d", | ||
3016 | (m->mdmreg[REG_SI1] & 1) ? 8 : 0); | ||
3017 | #ifdef CONFIG_ISDN_TTY_FAX | ||
3018 | if (TTY_IS_FCLASS2(info)) | ||
3019 | sprintf(rs, "\r\n2"); | ||
3020 | else if (TTY_IS_FCLASS1(info)) | ||
3021 | sprintf(rs, "\r\n1"); | ||
3022 | #endif | ||
3023 | isdn_tty_at_cout(rs, info); | ||
3024 | break; | ||
3025 | case '=': | ||
3026 | p[0]++; | ||
3027 | switch (*p[0]) { | ||
3028 | case '0': | ||
3029 | p[0]++; | ||
3030 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; | ||
3031 | m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS; | ||
3032 | m->mdmreg[REG_SI1] = 4; | ||
3033 | info->xmit_size = | ||
3034 | m->mdmreg[REG_PSIZE] * 16; | ||
3035 | break; | ||
3036 | #ifdef CONFIG_ISDN_TTY_FAX | ||
3037 | case '1': | ||
3038 | p[0]++; | ||
3039 | if (!(dev->global_features & | ||
3040 | ISDN_FEATURE_L3_FCLASS1)) | ||
3041 | PARSE_ERROR1; | ||
3042 | m->mdmreg[REG_SI1] = 1; | ||
3043 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX; | ||
3044 | m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS1; | ||
3045 | info->xmit_size = | ||
3046 | m->mdmreg[REG_PSIZE] * 16; | ||
3047 | break; | ||
3048 | case '2': | ||
3049 | p[0]++; | ||
3050 | if (!(dev->global_features & | ||
3051 | ISDN_FEATURE_L3_FCLASS2)) | ||
3052 | PARSE_ERROR1; | ||
3053 | m->mdmreg[REG_SI1] = 1; | ||
3054 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX; | ||
3055 | m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS2; | ||
3056 | info->xmit_size = | ||
3057 | m->mdmreg[REG_PSIZE] * 16; | ||
3058 | break; | ||
3059 | #endif | ||
3060 | case '8': | ||
3061 | p[0]++; | ||
3062 | /* L2 will change on dialout with si=1 */ | ||
3063 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; | ||
3064 | m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS; | ||
3065 | m->mdmreg[REG_SI1] = 5; | ||
3066 | info->xmit_size = VBUF; | ||
3067 | break; | ||
3068 | case '?': | ||
3069 | p[0]++; | ||
3070 | strcpy(rs, "\r\n0,"); | ||
3071 | #ifdef CONFIG_ISDN_TTY_FAX | ||
3072 | if (dev->global_features & | ||
3073 | ISDN_FEATURE_L3_FCLASS1) | ||
3074 | strcat(rs, "1,"); | ||
3075 | if (dev->global_features & | ||
3076 | ISDN_FEATURE_L3_FCLASS2) | ||
3077 | strcat(rs, "2,"); | ||
3078 | #endif | ||
3079 | strcat(rs, "8"); | ||
3080 | isdn_tty_at_cout(rs, info); | ||
3081 | break; | ||
3082 | default: | ||
3083 | PARSE_ERROR1; | ||
3084 | } | ||
3085 | break; | ||
3086 | default: | ||
3087 | PARSE_ERROR1; | ||
3088 | } | ||
3089 | return 0; | ||
3090 | } | ||
3091 | #ifdef CONFIG_ISDN_TTY_FAX | ||
3092 | return (isdn_tty_cmd_PLUSF_FAX(p, info)); | ||
3093 | #else | ||
3094 | PARSE_ERROR1; | ||
3095 | #endif | ||
3096 | } | ||
3097 | |||
3098 | /* | ||
3099 | * Parse AT+V.. commands | ||
3100 | */ | ||
3101 | static int | ||
3102 | isdn_tty_cmd_PLUSV(char **p, modem_info *info) | ||
3103 | { | ||
3104 | atemu *m = &info->emu; | ||
3105 | isdn_ctrl cmd; | ||
3106 | static char *vcmd[] = | ||
3107 | {"NH", "IP", "LS", "RX", "SD", "SM", "TX", "DD", NULL}; | ||
3108 | int i; | ||
3109 | int par1; | ||
3110 | int par2; | ||
3111 | char rs[20]; | ||
3112 | |||
3113 | i = 0; | ||
3114 | while (vcmd[i]) { | ||
3115 | if (!strncmp(vcmd[i], p[0], 2)) { | ||
3116 | p[0] += 2; | ||
3117 | break; | ||
3118 | } | ||
3119 | i++; | ||
3120 | } | ||
3121 | switch (i) { | ||
3122 | case 0: | ||
3123 | /* AT+VNH - Auto hangup feature */ | ||
3124 | switch (*p[0]) { | ||
3125 | case '?': | ||
3126 | p[0]++; | ||
3127 | isdn_tty_at_cout("\r\n1", info); | ||
3128 | break; | ||
3129 | case '=': | ||
3130 | p[0]++; | ||
3131 | switch (*p[0]) { | ||
3132 | case '1': | ||
3133 | p[0]++; | ||
3134 | break; | ||
3135 | case '?': | ||
3136 | p[0]++; | ||
3137 | isdn_tty_at_cout("\r\n1", info); | ||
3138 | break; | ||
3139 | default: | ||
3140 | PARSE_ERROR1; | ||
3141 | } | ||
3142 | break; | ||
3143 | default: | ||
3144 | PARSE_ERROR1; | ||
3145 | } | ||
3146 | break; | ||
3147 | case 1: | ||
3148 | /* AT+VIP - Reset all voice parameters */ | ||
3149 | isdn_tty_modem_reset_vpar(m); | ||
3150 | break; | ||
3151 | case 2: | ||
3152 | /* AT+VLS - Select device, accept incoming call */ | ||
3153 | switch (*p[0]) { | ||
3154 | case '?': | ||
3155 | p[0]++; | ||
3156 | sprintf(rs, "\r\n%d", m->vpar[0]); | ||
3157 | isdn_tty_at_cout(rs, info); | ||
3158 | break; | ||
3159 | case '=': | ||
3160 | p[0]++; | ||
3161 | switch (*p[0]) { | ||
3162 | case '0': | ||
3163 | p[0]++; | ||
3164 | m->vpar[0] = 0; | ||
3165 | break; | ||
3166 | case '2': | ||
3167 | p[0]++; | ||
3168 | m->vpar[0] = 2; | ||
3169 | break; | ||
3170 | case '?': | ||
3171 | p[0]++; | ||
3172 | isdn_tty_at_cout("\r\n0,2", info); | ||
3173 | break; | ||
3174 | default: | ||
3175 | PARSE_ERROR1; | ||
3176 | } | ||
3177 | break; | ||
3178 | default: | ||
3179 | PARSE_ERROR1; | ||
3180 | } | ||
3181 | break; | ||
3182 | case 3: | ||
3183 | /* AT+VRX - Start recording */ | ||
3184 | if (!m->vpar[0]) | ||
3185 | PARSE_ERROR1; | ||
3186 | if (info->online != 1) { | ||
3187 | isdn_tty_modem_result(RESULT_NO_ANSWER, info); | ||
3188 | return 1; | ||
3189 | } | ||
3190 | info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state); | ||
3191 | if (!info->dtmf_state) { | ||
3192 | printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n"); | ||
3193 | PARSE_ERROR1; | ||
3194 | } | ||
3195 | info->silence_state = isdn_audio_silence_init(info->silence_state); | ||
3196 | if (!info->silence_state) { | ||
3197 | printk(KERN_WARNING "isdn_tty: Couldn't malloc silence state\n"); | ||
3198 | PARSE_ERROR1; | ||
3199 | } | ||
3200 | if (m->vpar[3] < 5) { | ||
3201 | info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]); | ||
3202 | if (!info->adpcmr) { | ||
3203 | printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n"); | ||
3204 | PARSE_ERROR1; | ||
3205 | } | ||
3206 | } | ||
3207 | #ifdef ISDN_DEBUG_AT | ||
3208 | printk(KERN_DEBUG "AT: +VRX\n"); | ||
3209 | #endif | ||
3210 | info->vonline |= 1; | ||
3211 | isdn_tty_modem_result(RESULT_CONNECT, info); | ||
3212 | return 0; | ||
3213 | break; | ||
3214 | case 4: | ||
3215 | /* AT+VSD - Silence detection */ | ||
3216 | switch (*p[0]) { | ||
3217 | case '?': | ||
3218 | p[0]++; | ||
3219 | sprintf(rs, "\r\n<%d>,<%d>", | ||
3220 | m->vpar[1], | ||
3221 | m->vpar[2]); | ||
3222 | isdn_tty_at_cout(rs, info); | ||
3223 | break; | ||
3224 | case '=': | ||
3225 | p[0]++; | ||
3226 | if ((*p[0] >= '0') && (*p[0] <= '9')) { | ||
3227 | par1 = isdn_getnum(p); | ||
3228 | if ((par1 < 0) || (par1 > 31)) | ||
3229 | PARSE_ERROR1; | ||
3230 | if (*p[0] != ',') | ||
3231 | PARSE_ERROR1; | ||
3232 | p[0]++; | ||
3233 | par2 = isdn_getnum(p); | ||
3234 | if ((par2 < 0) || (par2 > 255)) | ||
3235 | PARSE_ERROR1; | ||
3236 | m->vpar[1] = par1; | ||
3237 | m->vpar[2] = par2; | ||
3238 | break; | ||
3239 | } else | ||
3240 | if (*p[0] == '?') { | ||
3241 | p[0]++; | ||
3242 | isdn_tty_at_cout("\r\n<0-31>,<0-255>", | ||
3243 | info); | ||
3244 | break; | ||
3245 | } else | ||
3246 | PARSE_ERROR1; | ||
3247 | break; | ||
3248 | default: | ||
3249 | PARSE_ERROR1; | ||
3250 | } | ||
3251 | break; | ||
3252 | case 5: | ||
3253 | /* AT+VSM - Select compression */ | ||
3254 | switch (*p[0]) { | ||
3255 | case '?': | ||
3256 | p[0]++; | ||
3257 | sprintf(rs, "\r\n<%d>,<%d><8000>", | ||
3258 | m->vpar[3], | ||
3259 | m->vpar[1]); | ||
3260 | isdn_tty_at_cout(rs, info); | ||
3261 | break; | ||
3262 | case '=': | ||
3263 | p[0]++; | ||
3264 | switch (*p[0]) { | ||
3265 | case '2': | ||
3266 | case '3': | ||
3267 | case '4': | ||
3268 | case '5': | ||
3269 | case '6': | ||
3270 | par1 = isdn_getnum(p); | ||
3271 | if ((par1 < 2) || (par1 > 6)) | ||
3272 | PARSE_ERROR1; | ||
3273 | m->vpar[3] = par1; | ||
3274 | break; | ||
3275 | case '?': | ||
3276 | p[0]++; | ||
3277 | isdn_tty_at_cout("\r\n2;ADPCM;2;0;(8000)\r\n", | ||
3278 | info); | ||
3279 | isdn_tty_at_cout("3;ADPCM;3;0;(8000)\r\n", | ||
3280 | info); | ||
3281 | isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n", | ||
3282 | info); | ||
3283 | isdn_tty_at_cout("5;ALAW;8;0;(8000)\r\n", | ||
3284 | info); | ||
3285 | isdn_tty_at_cout("6;ULAW;8;0;(8000)\r\n", | ||
3286 | info); | ||
3287 | break; | ||
3288 | default: | ||
3289 | PARSE_ERROR1; | ||
3290 | } | ||
3291 | break; | ||
3292 | default: | ||
3293 | PARSE_ERROR1; | ||
3294 | } | ||
3295 | break; | ||
3296 | case 6: | ||
3297 | /* AT+VTX - Start sending */ | ||
3298 | if (!m->vpar[0]) | ||
3299 | PARSE_ERROR1; | ||
3300 | if (info->online != 1) { | ||
3301 | isdn_tty_modem_result(RESULT_NO_ANSWER, info); | ||
3302 | return 1; | ||
3303 | } | ||
3304 | info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state); | ||
3305 | if (!info->dtmf_state) { | ||
3306 | printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n"); | ||
3307 | PARSE_ERROR1; | ||
3308 | } | ||
3309 | if (m->vpar[3] < 5) { | ||
3310 | info->adpcms = isdn_audio_adpcm_init(info->adpcms, m->vpar[3]); | ||
3311 | if (!info->adpcms) { | ||
3312 | printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n"); | ||
3313 | PARSE_ERROR1; | ||
3314 | } | ||
3315 | } | ||
3316 | #ifdef ISDN_DEBUG_AT | ||
3317 | printk(KERN_DEBUG "AT: +VTX\n"); | ||
3318 | #endif | ||
3319 | m->lastDLE = 0; | ||
3320 | info->vonline |= 2; | ||
3321 | isdn_tty_modem_result(RESULT_CONNECT, info); | ||
3322 | return 0; | ||
3323 | break; | ||
3324 | case 7: | ||
3325 | /* AT+VDD - DTMF detection */ | ||
3326 | switch (*p[0]) { | ||
3327 | case '?': | ||
3328 | p[0]++; | ||
3329 | sprintf(rs, "\r\n<%d>,<%d>", | ||
3330 | m->vpar[4], | ||
3331 | m->vpar[5]); | ||
3332 | isdn_tty_at_cout(rs, info); | ||
3333 | break; | ||
3334 | case '=': | ||
3335 | p[0]++; | ||
3336 | if ((*p[0] >= '0') && (*p[0] <= '9')) { | ||
3337 | if (info->online != 1) | ||
3338 | PARSE_ERROR1; | ||
3339 | par1 = isdn_getnum(p); | ||
3340 | if ((par1 < 0) || (par1 > 15)) | ||
3341 | PARSE_ERROR1; | ||
3342 | if (*p[0] != ',') | ||
3343 | PARSE_ERROR1; | ||
3344 | p[0]++; | ||
3345 | par2 = isdn_getnum(p); | ||
3346 | if ((par2 < 0) || (par2 > 255)) | ||
3347 | PARSE_ERROR1; | ||
3348 | m->vpar[4] = par1; | ||
3349 | m->vpar[5] = par2; | ||
3350 | cmd.driver = info->isdn_driver; | ||
3351 | cmd.command = ISDN_CMD_AUDIO; | ||
3352 | cmd.arg = info->isdn_channel + (ISDN_AUDIO_SETDD << 8); | ||
3353 | cmd.parm.num[0] = par1; | ||
3354 | cmd.parm.num[1] = par2; | ||
3355 | isdn_command(&cmd); | ||
3356 | break; | ||
3357 | } else | ||
3358 | if (*p[0] == '?') { | ||
3359 | p[0]++; | ||
3360 | isdn_tty_at_cout("\r\n<0-15>,<0-255>", | ||
3361 | info); | ||
3362 | break; | ||
3363 | } else | ||
3364 | PARSE_ERROR1; | ||
3365 | break; | ||
3366 | default: | ||
3367 | PARSE_ERROR1; | ||
3368 | } | ||
3369 | break; | ||
3370 | default: | ||
3371 | PARSE_ERROR1; | ||
3372 | } | ||
3373 | return 0; | ||
3374 | } | ||
3375 | #endif /* CONFIG_ISDN_AUDIO */ | ||
3376 | |||
3377 | /* | ||
3378 | * Parse and perform an AT-command-line. | ||
3379 | */ | ||
3380 | static void | ||
3381 | isdn_tty_parse_at(modem_info *info) | ||
3382 | { | ||
3383 | atemu *m = &info->emu; | ||
3384 | char *p; | ||
3385 | char ds[ISDN_MSNLEN]; | ||
3386 | |||
3387 | #ifdef ISDN_DEBUG_AT | ||
3388 | printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd); | ||
3389 | #endif | ||
3390 | for (p = &m->mdmcmd[2]; *p;) { | ||
3391 | switch (*p) { | ||
3392 | case ' ': | ||
3393 | p++; | ||
3394 | break; | ||
3395 | case 'A': | ||
3396 | /* A - Accept incoming call */ | ||
3397 | p++; | ||
3398 | isdn_tty_cmd_ATA(info); | ||
3399 | return; | ||
3400 | case 'D': | ||
3401 | /* D - Dial */ | ||
3402 | if (info->msr & UART_MSR_DCD) | ||
3403 | PARSE_ERROR; | ||
3404 | if (info->msr & UART_MSR_RI) { | ||
3405 | isdn_tty_modem_result(RESULT_NO_CARRIER, info); | ||
3406 | return; | ||
3407 | } | ||
3408 | isdn_tty_getdial(++p, ds, sizeof ds); | ||
3409 | p += strlen(p); | ||
3410 | if (!strlen(m->msn)) | ||
3411 | isdn_tty_modem_result(RESULT_NO_MSN_EAZ, info); | ||
3412 | else if (strlen(ds)) | ||
3413 | isdn_tty_dial(ds, info, m); | ||
3414 | else | ||
3415 | PARSE_ERROR; | ||
3416 | return; | ||
3417 | case 'E': | ||
3418 | /* E - Turn Echo on/off */ | ||
3419 | p++; | ||
3420 | switch (isdn_getnum(&p)) { | ||
3421 | case 0: | ||
3422 | m->mdmreg[REG_ECHO] &= ~BIT_ECHO; | ||
3423 | break; | ||
3424 | case 1: | ||
3425 | m->mdmreg[REG_ECHO] |= BIT_ECHO; | ||
3426 | break; | ||
3427 | default: | ||
3428 | PARSE_ERROR; | ||
3429 | } | ||
3430 | break; | ||
3431 | case 'H': | ||
3432 | /* H - On/Off-hook */ | ||
3433 | p++; | ||
3434 | switch (*p) { | ||
3435 | case '0': | ||
3436 | p++; | ||
3437 | isdn_tty_on_hook(info); | ||
3438 | break; | ||
3439 | case '1': | ||
3440 | p++; | ||
3441 | isdn_tty_off_hook(); | ||
3442 | break; | ||
3443 | default: | ||
3444 | isdn_tty_on_hook(info); | ||
3445 | break; | ||
3446 | } | ||
3447 | break; | ||
3448 | case 'I': | ||
3449 | /* I - Information */ | ||
3450 | p++; | ||
3451 | isdn_tty_at_cout("\r\nLinux ISDN", info); | ||
3452 | switch (*p) { | ||
3453 | case '0': | ||
3454 | case '1': | ||
3455 | p++; | ||
3456 | break; | ||
3457 | case '2': | ||
3458 | p++; | ||
3459 | isdn_tty_report(info); | ||
3460 | break; | ||
3461 | case '3': | ||
3462 | p++; | ||
3463 | snprintf(ds, sizeof(ds), "\r\n%d", info->emu.charge); | ||
3464 | isdn_tty_at_cout(ds, info); | ||
3465 | break; | ||
3466 | default:; | ||
3467 | } | ||
3468 | break; | ||
3469 | #ifdef DUMMY_HAYES_AT | ||
3470 | case 'L': | ||
3471 | case 'M': | ||
3472 | /* only for be compilant with common scripts */ | ||
3473 | /* no function */ | ||
3474 | p++; | ||
3475 | isdn_getnum(&p); | ||
3476 | break; | ||
3477 | #endif | ||
3478 | case 'O': | ||
3479 | /* O - Go online */ | ||
3480 | p++; | ||
3481 | if (info->msr & UART_MSR_DCD) | ||
3482 | /* if B-Channel is up */ | ||
3483 | isdn_tty_modem_result((m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) ? RESULT_CONNECT : RESULT_CONNECT64000, info); | ||
3484 | else | ||
3485 | isdn_tty_modem_result(RESULT_NO_CARRIER, info); | ||
3486 | return; | ||
3487 | case 'Q': | ||
3488 | /* Q - Turn Emulator messages on/off */ | ||
3489 | p++; | ||
3490 | switch (isdn_getnum(&p)) { | ||
3491 | case 0: | ||
3492 | m->mdmreg[REG_RESP] |= BIT_RESP; | ||
3493 | break; | ||
3494 | case 1: | ||
3495 | m->mdmreg[REG_RESP] &= ~BIT_RESP; | ||
3496 | break; | ||
3497 | default: | ||
3498 | PARSE_ERROR; | ||
3499 | } | ||
3500 | break; | ||
3501 | case 'S': | ||
3502 | /* S - Set/Get Register */ | ||
3503 | p++; | ||
3504 | if (isdn_tty_cmd_ATS(&p, info)) | ||
3505 | return; | ||
3506 | break; | ||
3507 | case 'V': | ||
3508 | /* V - Numeric or ASCII Emulator-messages */ | ||
3509 | p++; | ||
3510 | switch (isdn_getnum(&p)) { | ||
3511 | case 0: | ||
3512 | m->mdmreg[REG_RESP] |= BIT_RESPNUM; | ||
3513 | break; | ||
3514 | case 1: | ||
3515 | m->mdmreg[REG_RESP] &= ~BIT_RESPNUM; | ||
3516 | break; | ||
3517 | default: | ||
3518 | PARSE_ERROR; | ||
3519 | } | ||
3520 | break; | ||
3521 | case 'Z': | ||
3522 | /* Z - Load Registers from Profile */ | ||
3523 | p++; | ||
3524 | if (info->msr & UART_MSR_DCD) { | ||
3525 | info->online = 0; | ||
3526 | isdn_tty_on_hook(info); | ||
3527 | } | ||
3528 | isdn_tty_modem_reset_regs(info, 1); | ||
3529 | break; | ||
3530 | case '+': | ||
3531 | p++; | ||
3532 | switch (*p) { | ||
3533 | #ifdef CONFIG_ISDN_AUDIO | ||
3534 | case 'F': | ||
3535 | p++; | ||
3536 | if (isdn_tty_cmd_PLUSF(&p, info)) | ||
3537 | return; | ||
3538 | break; | ||
3539 | case 'V': | ||
3540 | if ((!(m->mdmreg[REG_SI1] & 1)) || | ||
3541 | (m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) | ||
3542 | PARSE_ERROR; | ||
3543 | p++; | ||
3544 | if (isdn_tty_cmd_PLUSV(&p, info)) | ||
3545 | return; | ||
3546 | break; | ||
3547 | #endif /* CONFIG_ISDN_AUDIO */ | ||
3548 | case 'S': /* SUSPEND */ | ||
3549 | p++; | ||
3550 | isdn_tty_get_msnstr(ds, &p); | ||
3551 | isdn_tty_suspend(ds, info, m); | ||
3552 | break; | ||
3553 | case 'R': /* RESUME */ | ||
3554 | p++; | ||
3555 | isdn_tty_get_msnstr(ds, &p); | ||
3556 | isdn_tty_resume(ds, info, m); | ||
3557 | break; | ||
3558 | case 'M': /* MESSAGE */ | ||
3559 | p++; | ||
3560 | isdn_tty_send_msg(info, m, p); | ||
3561 | break; | ||
3562 | default: | ||
3563 | PARSE_ERROR; | ||
3564 | } | ||
3565 | break; | ||
3566 | case '&': | ||
3567 | p++; | ||
3568 | if (isdn_tty_cmd_ATand(&p, info)) | ||
3569 | return; | ||
3570 | break; | ||
3571 | default: | ||
3572 | PARSE_ERROR; | ||
3573 | } | ||
3574 | } | ||
3575 | #ifdef CONFIG_ISDN_AUDIO | ||
3576 | if (!info->vonline) | ||
3577 | #endif | ||
3578 | isdn_tty_modem_result(RESULT_OK, info); | ||
3579 | } | ||
3580 | |||
3581 | /* Need own toupper() because standard-toupper is not available | ||
3582 | * within modules. | ||
3583 | */ | ||
3584 | #define my_toupper(c) (((c >= 'a') && (c <= 'z')) ? (c & 0xdf) : c) | ||
3585 | |||
3586 | /* | ||
3587 | * Perform line-editing of AT-commands | ||
3588 | * | ||
3589 | * Parameters: | ||
3590 | * p inputbuffer | ||
3591 | * count length of buffer | ||
3592 | * channel index to line (minor-device) | ||
3593 | */ | ||
3594 | static int | ||
3595 | isdn_tty_edit_at(const char *p, int count, modem_info *info) | ||
3596 | { | ||
3597 | atemu *m = &info->emu; | ||
3598 | int total = 0; | ||
3599 | u_char c; | ||
3600 | char eb[2]; | ||
3601 | int cnt; | ||
3602 | |||
3603 | for (cnt = count; cnt > 0; p++, cnt--) { | ||
3604 | c = *p; | ||
3605 | total++; | ||
3606 | if (c == m->mdmreg[REG_CR] || c == m->mdmreg[REG_LF]) { | ||
3607 | /* Separator (CR or LF) */ | ||
3608 | m->mdmcmd[m->mdmcmdl] = 0; | ||
3609 | if (m->mdmreg[REG_ECHO] & BIT_ECHO) { | ||
3610 | eb[0] = c; | ||
3611 | eb[1] = 0; | ||
3612 | isdn_tty_at_cout(eb, info); | ||
3613 | } | ||
3614 | if ((m->mdmcmdl >= 2) && (!(strncmp(m->mdmcmd, "AT", 2)))) | ||
3615 | isdn_tty_parse_at(info); | ||
3616 | m->mdmcmdl = 0; | ||
3617 | continue; | ||
3618 | } | ||
3619 | if (c == m->mdmreg[REG_BS] && m->mdmreg[REG_BS] < 128) { | ||
3620 | /* Backspace-Function */ | ||
3621 | if ((m->mdmcmdl > 2) || (!m->mdmcmdl)) { | ||
3622 | if (m->mdmcmdl) | ||
3623 | m->mdmcmdl--; | ||
3624 | if (m->mdmreg[REG_ECHO] & BIT_ECHO) | ||
3625 | isdn_tty_at_cout("\b", info); | ||
3626 | } | ||
3627 | continue; | ||
3628 | } | ||
3629 | if (cmdchar(c)) { | ||
3630 | if (m->mdmreg[REG_ECHO] & BIT_ECHO) { | ||
3631 | eb[0] = c; | ||
3632 | eb[1] = 0; | ||
3633 | isdn_tty_at_cout(eb, info); | ||
3634 | } | ||
3635 | if (m->mdmcmdl < 255) { | ||
3636 | c = my_toupper(c); | ||
3637 | switch (m->mdmcmdl) { | ||
3638 | case 1: | ||
3639 | if (c == 'T') { | ||
3640 | m->mdmcmd[m->mdmcmdl] = c; | ||
3641 | m->mdmcmd[++m->mdmcmdl] = 0; | ||
3642 | break; | ||
3643 | } else | ||
3644 | m->mdmcmdl = 0; | ||
3645 | /* Fall through - check for 'A' */ | ||
3646 | case 0: | ||
3647 | if (c == 'A') { | ||
3648 | m->mdmcmd[m->mdmcmdl] = c; | ||
3649 | m->mdmcmd[++m->mdmcmdl] = 0; | ||
3650 | } | ||
3651 | break; | ||
3652 | default: | ||
3653 | m->mdmcmd[m->mdmcmdl] = c; | ||
3654 | m->mdmcmd[++m->mdmcmdl] = 0; | ||
3655 | } | ||
3656 | } | ||
3657 | } | ||
3658 | } | ||
3659 | return total; | ||
3660 | } | ||
3661 | |||
3662 | /* | ||
3663 | * Switch all modem-channels who are online and got a valid | ||
3664 | * escape-sequence 1.5 seconds ago, to command-mode. | ||
3665 | * This function is called every second via timer-interrupt from within | ||
3666 | * timer-dispatcher isdn_timer_function() | ||
3667 | */ | ||
3668 | void | ||
3669 | isdn_tty_modem_escape(void) | ||
3670 | { | ||
3671 | int ton = 0; | ||
3672 | int i; | ||
3673 | int midx; | ||
3674 | |||
3675 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
3676 | if (USG_MODEM(dev->usage[i]) && (midx = dev->m_idx[i]) >= 0) { | ||
3677 | modem_info *info = &dev->mdm.info[midx]; | ||
3678 | if (info->online) { | ||
3679 | ton = 1; | ||
3680 | if ((info->emu.pluscount == 3) && | ||
3681 | time_after(jiffies, | ||
3682 | info->emu.lastplus + PLUSWAIT2)) { | ||
3683 | info->emu.pluscount = 0; | ||
3684 | info->online = 0; | ||
3685 | isdn_tty_modem_result(RESULT_OK, info); | ||
3686 | } | ||
3687 | } | ||
3688 | } | ||
3689 | isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, ton); | ||
3690 | } | ||
3691 | |||
3692 | /* | ||
3693 | * Put a RING-message to all modem-channels who have the RI-bit set. | ||
3694 | * This function is called every second via timer-interrupt from within | ||
3695 | * timer-dispatcher isdn_timer_function() | ||
3696 | */ | ||
3697 | void | ||
3698 | isdn_tty_modem_ring(void) | ||
3699 | { | ||
3700 | int ton = 0; | ||
3701 | int i; | ||
3702 | |||
3703 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
3704 | modem_info *info = &dev->mdm.info[i]; | ||
3705 | if (info->msr & UART_MSR_RI) { | ||
3706 | ton = 1; | ||
3707 | isdn_tty_modem_result(RESULT_RING, info); | ||
3708 | } | ||
3709 | } | ||
3710 | isdn_timer_ctrl(ISDN_TIMER_MODEMRING, ton); | ||
3711 | } | ||
3712 | |||
3713 | /* | ||
3714 | * For all online tty's, try sending data to | ||
3715 | * the lower levels. | ||
3716 | */ | ||
3717 | void | ||
3718 | isdn_tty_modem_xmit(void) | ||
3719 | { | ||
3720 | int ton = 1; | ||
3721 | int i; | ||
3722 | |||
3723 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
3724 | modem_info *info = &dev->mdm.info[i]; | ||
3725 | if (info->online) { | ||
3726 | ton = 1; | ||
3727 | isdn_tty_senddown(info); | ||
3728 | isdn_tty_tint(info); | ||
3729 | } | ||
3730 | } | ||
3731 | isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton); | ||
3732 | } | ||
3733 | |||
3734 | /* | ||
3735 | * Check all channels if we have a 'no carrier' timeout. | ||
3736 | * Timeout value is set by Register S7. | ||
3737 | */ | ||
3738 | void | ||
3739 | isdn_tty_carrier_timeout(void) | ||
3740 | { | ||
3741 | int ton = 0; | ||
3742 | int i; | ||
3743 | |||
3744 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
3745 | modem_info *info = &dev->mdm.info[i]; | ||
3746 | if (!info->dialing) | ||
3747 | continue; | ||
3748 | if (info->emu.carrierwait++ > info->emu.mdmreg[REG_WAITC]) { | ||
3749 | info->dialing = 0; | ||
3750 | isdn_tty_modem_result(RESULT_NO_CARRIER, info); | ||
3751 | isdn_tty_modem_hup(info, 1); | ||
3752 | } else | ||
3753 | ton = 1; | ||
3754 | } | ||
3755 | isdn_timer_ctrl(ISDN_TIMER_CARRIER, ton); | ||
3756 | } | ||
diff --git a/drivers/isdn/i4l/isdn_tty.h b/drivers/isdn/i4l/isdn_tty.h deleted file mode 100644 index a6f801d2263b..000000000000 --- a/drivers/isdn/i4l/isdn_tty.h +++ /dev/null | |||
@@ -1,120 +0,0 @@ | |||
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 | |||
14 | #define DLE 0x10 | ||
15 | #define ETX 0x03 | ||
16 | #define DC4 0x14 | ||
17 | |||
18 | |||
19 | /* | ||
20 | * Definition of some special Registers of AT-Emulator | ||
21 | */ | ||
22 | #define REG_RINGATA 0 | ||
23 | #define REG_RINGCNT 1 /* ring counter register */ | ||
24 | #define REG_ESC 2 | ||
25 | #define REG_CR 3 | ||
26 | #define REG_LF 4 | ||
27 | #define REG_BS 5 | ||
28 | |||
29 | #define REG_WAITC 7 | ||
30 | |||
31 | #define REG_RESP 12 /* show response messages register */ | ||
32 | #define BIT_RESP 1 /* show response messages bit */ | ||
33 | #define REG_RESPNUM 12 /* show numeric responses register */ | ||
34 | #define BIT_RESPNUM 2 /* show numeric responses bit */ | ||
35 | #define REG_ECHO 12 | ||
36 | #define BIT_ECHO 4 | ||
37 | #define REG_DCD 12 | ||
38 | #define BIT_DCD 8 | ||
39 | #define REG_CTS 12 | ||
40 | #define BIT_CTS 16 | ||
41 | #define REG_DTRR 12 | ||
42 | #define BIT_DTRR 32 | ||
43 | #define REG_DSR 12 | ||
44 | #define BIT_DSR 64 | ||
45 | #define REG_CPPP 12 | ||
46 | #define BIT_CPPP 128 | ||
47 | |||
48 | #define REG_DXMT 13 | ||
49 | #define BIT_DXMT 1 | ||
50 | #define REG_T70 13 | ||
51 | #define BIT_T70 2 | ||
52 | #define BIT_T70_EXT 32 | ||
53 | #define REG_DTRHUP 13 | ||
54 | #define BIT_DTRHUP 4 | ||
55 | #define REG_RESPXT 13 | ||
56 | #define BIT_RESPXT 8 | ||
57 | #define REG_CIDONCE 13 | ||
58 | #define BIT_CIDONCE 16 | ||
59 | #define REG_RUNG 13 /* show RUNG message register */ | ||
60 | #define BIT_RUNG 64 /* show RUNG message bit */ | ||
61 | #define REG_DISPLAY 13 | ||
62 | #define BIT_DISPLAY 128 | ||
63 | |||
64 | #define REG_L2PROT 14 | ||
65 | #define REG_L3PROT 15 | ||
66 | #define REG_PSIZE 16 | ||
67 | #define REG_WSIZE 17 | ||
68 | #define REG_SI1 18 | ||
69 | #define REG_SI2 19 | ||
70 | #define REG_SI1I 20 | ||
71 | #define REG_PLAN 21 | ||
72 | #define REG_SCREEN 22 | ||
73 | |||
74 | #define REG_CPN 23 | ||
75 | #define BIT_CPN 1 | ||
76 | #define REG_CPNFCON 23 | ||
77 | #define BIT_CPNFCON 2 | ||
78 | #define REG_CDN 23 | ||
79 | #define BIT_CDN 4 | ||
80 | |||
81 | /* defines for result codes */ | ||
82 | #define RESULT_OK 0 | ||
83 | #define RESULT_CONNECT 1 | ||
84 | #define RESULT_RING 2 | ||
85 | #define RESULT_NO_CARRIER 3 | ||
86 | #define RESULT_ERROR 4 | ||
87 | #define RESULT_CONNECT64000 5 | ||
88 | #define RESULT_NO_DIALTONE 6 | ||
89 | #define RESULT_BUSY 7 | ||
90 | #define RESULT_NO_ANSWER 8 | ||
91 | #define RESULT_RINGING 9 | ||
92 | #define RESULT_NO_MSN_EAZ 10 | ||
93 | #define RESULT_VCON 11 | ||
94 | #define RESULT_RUNG 12 | ||
95 | |||
96 | #define TTY_IS_FCLASS1(info) \ | ||
97 | ((info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) && \ | ||
98 | (info->emu.mdmreg[REG_L3PROT] == ISDN_PROTO_L3_FCLASS1)) | ||
99 | #define TTY_IS_FCLASS2(info) \ | ||
100 | ((info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) && \ | ||
101 | (info->emu.mdmreg[REG_L3PROT] == ISDN_PROTO_L3_FCLASS2)) | ||
102 | |||
103 | extern void isdn_tty_modem_escape(void); | ||
104 | extern void isdn_tty_modem_ring(void); | ||
105 | extern void isdn_tty_carrier_timeout(void); | ||
106 | extern void isdn_tty_modem_xmit(void); | ||
107 | extern int isdn_tty_modem_init(void); | ||
108 | extern void isdn_tty_exit(void); | ||
109 | extern void isdn_tty_readmodem(void); | ||
110 | extern int isdn_tty_find_icall(int, int, setup_parm *); | ||
111 | extern int isdn_tty_stat_callback(int, isdn_ctrl *); | ||
112 | extern int isdn_tty_rcv_skb(int, int, int, struct sk_buff *); | ||
113 | extern int isdn_tty_capi_facility(capi_msg *cm); | ||
114 | extern void isdn_tty_at_cout(char *, modem_info *); | ||
115 | extern void isdn_tty_modem_hup(modem_info *, int); | ||
116 | #ifdef CONFIG_ISDN_TTY_FAX | ||
117 | extern int isdn_tty_cmd_PLUSF_FAX(char **, modem_info *); | ||
118 | extern int isdn_tty_fax_command(modem_info *, isdn_ctrl *); | ||
119 | extern void isdn_tty_fax_bitorder(modem_info *, struct sk_buff *); | ||
120 | #endif | ||
diff --git a/drivers/isdn/i4l/isdn_ttyfax.c b/drivers/isdn/i4l/isdn_ttyfax.c deleted file mode 100644 index 47aae4916730..000000000000 --- a/drivers/isdn/i4l/isdn_ttyfax.c +++ /dev/null | |||
@@ -1,1123 +0,0 @@ | |||
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 | static 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 | static 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 | static 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 (threshold 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) || | ||
910 | (!(info->faxonline & 1))) | ||
911 | PARSE_ERROR1; | ||
912 | par = isdn_getnum(p); | ||
913 | if ((par < 0) || (par > 2)) | ||
914 | PARSE_ERROR1; | ||
915 | f->fet = par; | ||
916 | f->code = ISDN_TTY_FAX_ET; | ||
917 | cmd.driver = info->isdn_driver; | ||
918 | cmd.arg = info->isdn_channel; | ||
919 | cmd.command = ISDN_CMD_FAXCMD; | ||
920 | isdn_command(&cmd); | ||
921 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
922 | printk(KERN_DEBUG "isdn_tty: Fax FET=%d\n", par); | ||
923 | #endif | ||
924 | return 1; | ||
925 | } | ||
926 | return 0; | ||
927 | } | ||
928 | /* K - terminate */ | ||
929 | if (!strncmp(p[0], "K", 1)) { | ||
930 | p[0] += 1; | ||
931 | if ((f->phase == ISDN_FAX_PHASE_IDLE) || (f->phase == ISDN_FAX_PHASE_E)) | ||
932 | PARSE_ERROR1; | ||
933 | isdn_tty_modem_hup(info, 1); | ||
934 | return 1; | ||
935 | } | ||
936 | /* LID=string - local fax ID */ | ||
937 | if (!strncmp(p[0], "LID", 3)) { | ||
938 | int i, r; | ||
939 | p[0] += 3; | ||
940 | switch (*p[0]) { | ||
941 | case '?': | ||
942 | p[0]++; | ||
943 | sprintf(rs, "\r\n\"%s\"", f->id); | ||
944 | isdn_tty_at_cout(rs, info); | ||
945 | break; | ||
946 | case '=': | ||
947 | p[0]++; | ||
948 | if (*p[0] == '?') { | ||
949 | p[0]++; | ||
950 | sprintf(rs, "\r\n\"STRING\""); | ||
951 | isdn_tty_at_cout(rs, info); | ||
952 | } else { | ||
953 | if (*p[0] == '"') | ||
954 | p[0]++; | ||
955 | for (i = 0; (*p[0]) && i < (FAXIDLEN - 1) && (*p[0] != '"'); i++) { | ||
956 | f->id[i] = *p[0]++; | ||
957 | } | ||
958 | if (*p[0] == '"') | ||
959 | p[0]++; | ||
960 | for (r = i; r < FAXIDLEN; r++) { | ||
961 | f->id[r] = 32; | ||
962 | } | ||
963 | f->id[FAXIDLEN - 1] = 0; | ||
964 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
965 | printk(KERN_DEBUG "isdn_tty: Fax local ID \"%s\"\n", f->id); | ||
966 | #endif | ||
967 | } | ||
968 | break; | ||
969 | default: | ||
970 | PARSE_ERROR1; | ||
971 | } | ||
972 | return 0; | ||
973 | } | ||
974 | |||
975 | /* MDL? - DCE Model */ | ||
976 | if (!strncmp(p[0], "MDL?", 4)) { | ||
977 | p[0] += 4; | ||
978 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
979 | printk(KERN_DEBUG "isdn_tty: FMDL?\n"); | ||
980 | #endif | ||
981 | isdn_tty_at_cout("\r\nisdn4linux", info); | ||
982 | return 0; | ||
983 | } | ||
984 | /* MFR? - DCE Manufacturer */ | ||
985 | if (!strncmp(p[0], "MFR?", 4)) { | ||
986 | p[0] += 4; | ||
987 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
988 | printk(KERN_DEBUG "isdn_tty: FMFR?\n"); | ||
989 | #endif | ||
990 | isdn_tty_at_cout("\r\nisdn4linux", info); | ||
991 | return 0; | ||
992 | } | ||
993 | /* MINSP=n - Minimum Speed for Phase C */ | ||
994 | if (!strncmp(p[0], "MINSP", 5)) { | ||
995 | p[0] += 5; | ||
996 | switch (*p[0]) { | ||
997 | case '?': | ||
998 | p[0]++; | ||
999 | sprintf(rs, "\r\n%d", f->minsp); | ||
1000 | isdn_tty_at_cout(rs, info); | ||
1001 | break; | ||
1002 | case '=': | ||
1003 | p[0]++; | ||
1004 | if (*p[0] == '?') { | ||
1005 | p[0]++; | ||
1006 | sprintf(rs, "\r\n0-5"); | ||
1007 | isdn_tty_at_cout(rs, info); | ||
1008 | } else { | ||
1009 | par = isdn_getnum(p); | ||
1010 | if ((par < 0) || (par > 5)) | ||
1011 | PARSE_ERROR1; | ||
1012 | f->minsp = par; | ||
1013 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
1014 | printk(KERN_DEBUG "isdn_tty: Fax FMINSP=%d\n", par); | ||
1015 | #endif | ||
1016 | } | ||
1017 | break; | ||
1018 | default: | ||
1019 | PARSE_ERROR1; | ||
1020 | } | ||
1021 | return 0; | ||
1022 | } | ||
1023 | /* PHCTO=value - DTE phase C timeout */ | ||
1024 | if (!strncmp(p[0], "PHCTO", 5)) { | ||
1025 | p[0] += 5; | ||
1026 | switch (*p[0]) { | ||
1027 | case '?': | ||
1028 | p[0]++; | ||
1029 | sprintf(rs, "\r\n%d", f->phcto); | ||
1030 | isdn_tty_at_cout(rs, info); | ||
1031 | break; | ||
1032 | case '=': | ||
1033 | p[0]++; | ||
1034 | if (*p[0] == '?') { | ||
1035 | p[0]++; | ||
1036 | sprintf(rs, "\r\n0-255"); | ||
1037 | isdn_tty_at_cout(rs, info); | ||
1038 | } else { | ||
1039 | par = isdn_getnum(p); | ||
1040 | if ((par < 0) || (par > 255)) | ||
1041 | PARSE_ERROR1; | ||
1042 | f->phcto = par; | ||
1043 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
1044 | printk(KERN_DEBUG "isdn_tty: Fax FPHCTO=%d\n", par); | ||
1045 | #endif | ||
1046 | } | ||
1047 | break; | ||
1048 | default: | ||
1049 | PARSE_ERROR1; | ||
1050 | } | ||
1051 | return 0; | ||
1052 | } | ||
1053 | |||
1054 | /* REL=n - Phase C received EOL alignment */ | ||
1055 | if (!strncmp(p[0], "REL", 3)) { | ||
1056 | p[0] += 3; | ||
1057 | switch (*p[0]) { | ||
1058 | case '?': | ||
1059 | p[0]++; | ||
1060 | sprintf(rs, "\r\n%d", f->rel); | ||
1061 | isdn_tty_at_cout(rs, info); | ||
1062 | break; | ||
1063 | case '=': | ||
1064 | p[0]++; | ||
1065 | if (*p[0] == '?') { | ||
1066 | p[0]++; | ||
1067 | sprintf(rs, "\r\n0,1"); | ||
1068 | isdn_tty_at_cout(rs, info); | ||
1069 | } else { | ||
1070 | par = isdn_getnum(p); | ||
1071 | if ((par < 0) || (par > 1)) | ||
1072 | PARSE_ERROR1; | ||
1073 | f->rel = par; | ||
1074 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
1075 | printk(KERN_DEBUG "isdn_tty: Fax FREL=%d\n", par); | ||
1076 | #endif | ||
1077 | } | ||
1078 | break; | ||
1079 | default: | ||
1080 | PARSE_ERROR1; | ||
1081 | } | ||
1082 | return 0; | ||
1083 | } | ||
1084 | /* REV? - DCE Revision */ | ||
1085 | if (!strncmp(p[0], "REV?", 4)) { | ||
1086 | p[0] += 4; | ||
1087 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
1088 | printk(KERN_DEBUG "isdn_tty: FREV?\n"); | ||
1089 | #endif | ||
1090 | strcpy(rss, isdn_tty_fax_revision); | ||
1091 | sprintf(rs, "\r\nRev: %s", isdn_getrev(rss)); | ||
1092 | isdn_tty_at_cout(rs, info); | ||
1093 | return 0; | ||
1094 | } | ||
1095 | |||
1096 | /* Phase C Transmit Data Block Size */ | ||
1097 | if (!strncmp(p[0], "TBC=", 4)) { /* dummy, not used */ | ||
1098 | p[0] += 4; | ||
1099 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
1100 | printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]); | ||
1101 | #endif | ||
1102 | switch (*p[0]) { | ||
1103 | case '0': | ||
1104 | p[0]++; | ||
1105 | break; | ||
1106 | default: | ||
1107 | PARSE_ERROR1; | ||
1108 | } | ||
1109 | return 0; | ||
1110 | } | ||
1111 | printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]); | ||
1112 | PARSE_ERROR1; | ||
1113 | } | ||
1114 | |||
1115 | int | ||
1116 | isdn_tty_cmd_PLUSF_FAX(char **p, modem_info *info) | ||
1117 | { | ||
1118 | if (TTY_IS_FCLASS2(info)) | ||
1119 | return (isdn_tty_cmd_FCLASS2(p, info)); | ||
1120 | else if (TTY_IS_FCLASS1(info)) | ||
1121 | return (isdn_tty_cmd_FCLASS1(p, info)); | ||
1122 | PARSE_ERROR1; | ||
1123 | } | ||
diff --git a/drivers/isdn/i4l/isdn_ttyfax.h b/drivers/isdn/i4l/isdn_ttyfax.h deleted file mode 100644 index ccda4fcf8f7b..000000000000 --- a/drivers/isdn/i4l/isdn_ttyfax.h +++ /dev/null | |||
@@ -1,17 +0,0 @@ | |||
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 | ||
diff --git a/drivers/isdn/i4l/isdn_v110.c b/drivers/isdn/i4l/isdn_v110.c deleted file mode 100644 index d11fe76f138f..000000000000 --- a/drivers/isdn/i4l/isdn_v110.c +++ /dev/null | |||
@@ -1,625 +0,0 @@ | |||
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 = kzalloc(sizeof(isdn_v110_stream), GFP_ATOMIC)) == NULL) | ||
96 | return NULL; | ||
97 | v->key = key; | ||
98 | v->nbits = 0; | ||
99 | for (i = 0; key & (1 << i); i++) | ||
100 | v->nbits++; | ||
101 | |||
102 | v->nbytes = 8 / v->nbits; | ||
103 | v->decodelen = 0; | ||
104 | |||
105 | switch (key) { | ||
106 | case V110_38400: | ||
107 | v->OnlineFrame = V110_OnMatrix_38400; | ||
108 | v->OfflineFrame = V110_OffMatrix_38400; | ||
109 | break; | ||
110 | case V110_19200: | ||
111 | v->OnlineFrame = V110_OnMatrix_19200; | ||
112 | v->OfflineFrame = V110_OffMatrix_19200; | ||
113 | break; | ||
114 | default: | ||
115 | v->OnlineFrame = V110_OnMatrix_9600; | ||
116 | v->OfflineFrame = V110_OffMatrix_9600; | ||
117 | break; | ||
118 | } | ||
119 | v->framelen = v->nbytes * 10; | ||
120 | v->SyncInit = 5; | ||
121 | v->introducer = 0; | ||
122 | v->dbit = 1; | ||
123 | v->b = 0; | ||
124 | v->skbres = hdrlen; | ||
125 | v->maxsize = maxsize - hdrlen; | ||
126 | if ((v->encodebuf = kmalloc(maxsize, GFP_ATOMIC)) == NULL) { | ||
127 | kfree(v); | ||
128 | return NULL; | ||
129 | } | ||
130 | return v; | ||
131 | } | ||
132 | |||
133 | /* isdn_v110_close frees private V.110 data structures */ | ||
134 | void | ||
135 | isdn_v110_close(isdn_v110_stream *v) | ||
136 | { | ||
137 | if (v == NULL) | ||
138 | return; | ||
139 | #ifdef ISDN_V110_DEBUG | ||
140 | printk(KERN_DEBUG "v110 close\n"); | ||
141 | #endif | ||
142 | kfree(v->encodebuf); | ||
143 | kfree(v); | ||
144 | } | ||
145 | |||
146 | |||
147 | /* | ||
148 | * ValidHeaderBytes return the number of valid bytes in v->decodebuf | ||
149 | */ | ||
150 | static int | ||
151 | ValidHeaderBytes(isdn_v110_stream *v) | ||
152 | { | ||
153 | int i; | ||
154 | for (i = 0; (i < v->decodelen) && (i < v->nbytes); i++) | ||
155 | if ((v->decodebuf[i] & v->key) != 0) | ||
156 | break; | ||
157 | return i; | ||
158 | } | ||
159 | |||
160 | /* | ||
161 | * SyncHeader moves the decodebuf ptr to the next valid header | ||
162 | */ | ||
163 | static void | ||
164 | SyncHeader(isdn_v110_stream *v) | ||
165 | { | ||
166 | unsigned char *rbuf = v->decodebuf; | ||
167 | int len = v->decodelen; | ||
168 | |||
169 | if (len == 0) | ||
170 | return; | ||
171 | for (rbuf++, len--; len > 0; len--, rbuf++) /* such den SyncHeader in buf ! */ | ||
172 | if ((*rbuf & v->key) == 0) /* erstes byte gefunden ? */ | ||
173 | break; /* jupp! */ | ||
174 | if (len) | ||
175 | memcpy(v->decodebuf, rbuf, len); | ||
176 | |||
177 | v->decodelen = len; | ||
178 | #ifdef ISDN_V110_DEBUG | ||
179 | printk(KERN_DEBUG "isdn_v110: Header resync\n"); | ||
180 | #endif | ||
181 | } | ||
182 | |||
183 | /* DecodeMatrix takes n (n>=1) matrices (v110 frames, 10 bytes) where | ||
184 | len is the number of matrix-lines. len must be a multiple of 10, i.e. | ||
185 | only complete matices must be given. | ||
186 | From these, netto data is extracted and returned in buf. The return-value | ||
187 | is the bytecount of the decoded data. | ||
188 | */ | ||
189 | static int | ||
190 | DecodeMatrix(isdn_v110_stream *v, unsigned char *m, int len, unsigned char *buf) | ||
191 | { | ||
192 | int line = 0; | ||
193 | int buflen = 0; | ||
194 | int mbit = 64; | ||
195 | int introducer = v->introducer; | ||
196 | int dbit = v->dbit; | ||
197 | unsigned char b = v->b; | ||
198 | |||
199 | while (line < len) { /* Are we done with all lines of the matrix? */ | ||
200 | if ((line % 10) == 0) { /* the 0. line of the matrix is always 0 ! */ | ||
201 | if (m[line] != 0x00) { /* not 0 ? -> error! */ | ||
202 | #ifdef ISDN_V110_DEBUG | ||
203 | printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad Header\n"); | ||
204 | /* returning now is not the right thing, though :-( */ | ||
205 | #endif | ||
206 | } | ||
207 | line++; /* next line of matrix */ | ||
208 | continue; | ||
209 | } else if ((line % 10) == 5) { /* in line 5 there's only e-bits ! */ | ||
210 | if ((m[line] & 0x70) != 0x30) { /* 011 has to be at the beginning! */ | ||
211 | #ifdef ISDN_V110_DEBUG | ||
212 | printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad 5th line\n"); | ||
213 | /* returning now is not the right thing, though :-( */ | ||
214 | #endif | ||
215 | } | ||
216 | line++; /* next line */ | ||
217 | continue; | ||
218 | } else if (!introducer) { /* every byte starts with 10 (stopbit, startbit) */ | ||
219 | introducer = (m[line] & mbit) ? 0 : 1; /* current bit of the matrix */ | ||
220 | next_byte: | ||
221 | if (mbit > 2) { /* was it the last bit in this line ? */ | ||
222 | mbit >>= 1; /* no -> take next */ | ||
223 | continue; | ||
224 | } /* otherwise start with leftmost bit in the next line */ | ||
225 | mbit = 64; | ||
226 | line++; | ||
227 | continue; | ||
228 | } else { /* otherwise we need to set a data bit */ | ||
229 | if (m[line] & mbit) /* was that bit set in the matrix ? */ | ||
230 | b |= dbit; /* yes -> set it in the data byte */ | ||
231 | else | ||
232 | b &= dbit - 1; /* no -> clear it in the data byte */ | ||
233 | if (dbit < 128) /* is that data byte done ? */ | ||
234 | dbit <<= 1; /* no, got the next bit */ | ||
235 | else { /* data byte is done */ | ||
236 | buf[buflen++] = b; /* copy byte into the output buffer */ | ||
237 | introducer = b = 0; /* init of the intro sequence and of the data byte */ | ||
238 | dbit = 1; /* next we look for the 0th bit */ | ||
239 | } | ||
240 | goto next_byte; /* look for next bit in the matrix */ | ||
241 | } | ||
242 | } | ||
243 | v->introducer = introducer; | ||
244 | v->dbit = dbit; | ||
245 | v->b = b; | ||
246 | return buflen; /* return number of bytes in the output buffer */ | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * DecodeStream receives V.110 coded data from the input stream. It recovers the | ||
251 | * original frames. | ||
252 | * The input stream doesn't need to be framed | ||
253 | */ | ||
254 | struct sk_buff * | ||
255 | isdn_v110_decode(isdn_v110_stream *v, struct sk_buff *skb) | ||
256 | { | ||
257 | int i; | ||
258 | int j; | ||
259 | int len; | ||
260 | unsigned char *v110_buf; | ||
261 | unsigned char *rbuf; | ||
262 | |||
263 | if (!skb) { | ||
264 | printk(KERN_WARNING "isdn_v110_decode called with NULL skb!\n"); | ||
265 | return NULL; | ||
266 | } | ||
267 | rbuf = skb->data; | ||
268 | len = skb->len; | ||
269 | if (v == NULL) { | ||
270 | /* invalid handle, no chance to proceed */ | ||
271 | printk(KERN_WARNING "isdn_v110_decode called with NULL stream!\n"); | ||
272 | dev_kfree_skb(skb); | ||
273 | return NULL; | ||
274 | } | ||
275 | if (v->decodelen == 0) /* cache empty? */ | ||
276 | for (; len > 0; len--, rbuf++) /* scan for SyncHeader in buf */ | ||
277 | if ((*rbuf & v->key) == 0) | ||
278 | break; /* found first byte */ | ||
279 | if (len == 0) { | ||
280 | dev_kfree_skb(skb); | ||
281 | return NULL; | ||
282 | } | ||
283 | /* copy new data to decode-buffer */ | ||
284 | memcpy(&(v->decodebuf[v->decodelen]), rbuf, len); | ||
285 | v->decodelen += len; | ||
286 | ReSync: | ||
287 | if (v->decodelen < v->nbytes) { /* got a new header ? */ | ||
288 | dev_kfree_skb(skb); | ||
289 | return NULL; /* no, try later */ | ||
290 | } | ||
291 | if (ValidHeaderBytes(v) != v->nbytes) { /* is that a valid header? */ | ||
292 | SyncHeader(v); /* no -> look for header */ | ||
293 | goto ReSync; | ||
294 | } | ||
295 | len = (v->decodelen - (v->decodelen % (10 * v->nbytes))) / v->nbytes; | ||
296 | if ((v110_buf = kmalloc(len, GFP_ATOMIC)) == NULL) { | ||
297 | printk(KERN_WARNING "isdn_v110_decode: Couldn't allocate v110_buf\n"); | ||
298 | dev_kfree_skb(skb); | ||
299 | return NULL; | ||
300 | } | ||
301 | for (i = 0; i < len; i++) { | ||
302 | v110_buf[i] = 0; | ||
303 | for (j = 0; j < v->nbytes; j++) | ||
304 | v110_buf[i] |= (v->decodebuf[(i * v->nbytes) + j] & v->key) << (8 - ((j + 1) * v->nbits)); | ||
305 | v110_buf[i] = FlipBits(v110_buf[i], v->nbits); | ||
306 | } | ||
307 | v->decodelen = (v->decodelen % (10 * v->nbytes)); | ||
308 | memcpy(v->decodebuf, &(v->decodebuf[len * v->nbytes]), v->decodelen); | ||
309 | |||
310 | skb_trim(skb, DecodeMatrix(v, v110_buf, len, skb->data)); | ||
311 | kfree(v110_buf); | ||
312 | if (skb->len) | ||
313 | return skb; | ||
314 | else { | ||
315 | kfree_skb(skb); | ||
316 | return NULL; | ||
317 | } | ||
318 | } | ||
319 | |||
320 | /* EncodeMatrix takes input data in buf, len is the bytecount. | ||
321 | Data is encoded into v110 frames in m. Return value is the number of | ||
322 | matrix-lines generated. | ||
323 | */ | ||
324 | static int | ||
325 | EncodeMatrix(unsigned char *buf, int len, unsigned char *m, int mlen) | ||
326 | { | ||
327 | int line = 0; | ||
328 | int i = 0; | ||
329 | int mbit = 128; | ||
330 | int dbit = 1; | ||
331 | int introducer = 3; | ||
332 | int ibit[] = {0, 1, 1}; | ||
333 | |||
334 | while ((i < len) && (line < mlen)) { /* while we still have input data */ | ||
335 | switch (line % 10) { /* in which line of the matrix are we? */ | ||
336 | case 0: | ||
337 | m[line++] = 0x00; /* line 0 is always 0 */ | ||
338 | mbit = 128; /* go on with the 7th bit */ | ||
339 | break; | ||
340 | case 5: | ||
341 | m[line++] = 0xbf; /* line 5 is always 10111111 */ | ||
342 | mbit = 128; /* go on with the 7th bit */ | ||
343 | break; | ||
344 | } | ||
345 | if (line >= mlen) { | ||
346 | printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n"); | ||
347 | return line; | ||
348 | } | ||
349 | next_bit: | ||
350 | switch (mbit) { /* leftmost or rightmost bit ? */ | ||
351 | case 1: | ||
352 | line++; /* rightmost -> go to next line */ | ||
353 | if (line >= mlen) { | ||
354 | printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n"); | ||
355 | return line; | ||
356 | } | ||
357 | /* fall through */ | ||
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 | /* fall through */ | ||
391 | case 2: | ||
392 | m[line++] = 0xfe; | ||
393 | /* fall through */ | ||
394 | case 3: | ||
395 | m[line++] = 0xfe; | ||
396 | /* fall through */ | ||
397 | case 4: | ||
398 | m[line++] = 0xfe; | ||
399 | /* fall through */ | ||
400 | case 5: | ||
401 | m[line++] = 0xbf; | ||
402 | /* fall through */ | ||
403 | case 6: | ||
404 | m[line++] = 0xfe; | ||
405 | /* fall through */ | ||
406 | case 7: | ||
407 | m[line++] = 0xfe; | ||
408 | /* fall through */ | ||
409 | case 8: | ||
410 | m[line++] = 0xfe; | ||
411 | /* fall through */ | ||
412 | case 9: | ||
413 | m[line++] = 0xfe; | ||
414 | } | ||
415 | return line; /* that's how many lines we have */ | ||
416 | } | ||
417 | |||
418 | /* | ||
419 | * Build a sync frame. | ||
420 | */ | ||
421 | static struct sk_buff * | ||
422 | isdn_v110_sync(isdn_v110_stream *v) | ||
423 | { | ||
424 | struct sk_buff *skb; | ||
425 | |||
426 | if (v == NULL) { | ||
427 | /* invalid handle, no chance to proceed */ | ||
428 | printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n"); | ||
429 | return NULL; | ||
430 | } | ||
431 | if ((skb = dev_alloc_skb(v->framelen + v->skbres))) { | ||
432 | skb_reserve(skb, v->skbres); | ||
433 | skb_put_data(skb, v->OfflineFrame, v->framelen); | ||
434 | } | ||
435 | return skb; | ||
436 | } | ||
437 | |||
438 | /* | ||
439 | * Build an idle frame. | ||
440 | */ | ||
441 | static struct sk_buff * | ||
442 | isdn_v110_idle(isdn_v110_stream *v) | ||
443 | { | ||
444 | struct sk_buff *skb; | ||
445 | |||
446 | if (v == NULL) { | ||
447 | /* invalid handle, no chance to proceed */ | ||
448 | printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n"); | ||
449 | return NULL; | ||
450 | } | ||
451 | if ((skb = dev_alloc_skb(v->framelen + v->skbres))) { | ||
452 | skb_reserve(skb, v->skbres); | ||
453 | skb_put_data(skb, v->OnlineFrame, v->framelen); | ||
454 | } | ||
455 | return skb; | ||
456 | } | ||
457 | |||
458 | struct sk_buff * | ||
459 | isdn_v110_encode(isdn_v110_stream *v, struct sk_buff *skb) | ||
460 | { | ||
461 | int i; | ||
462 | int j; | ||
463 | int rlen; | ||
464 | int mlen; | ||
465 | int olen; | ||
466 | int size; | ||
467 | int sval1; | ||
468 | int sval2; | ||
469 | int nframes; | ||
470 | unsigned char *v110buf; | ||
471 | unsigned char *rbuf; | ||
472 | struct sk_buff *nskb; | ||
473 | |||
474 | if (v == NULL) { | ||
475 | /* invalid handle, no chance to proceed */ | ||
476 | printk(KERN_WARNING "isdn_v110_encode called with NULL stream!\n"); | ||
477 | return NULL; | ||
478 | } | ||
479 | if (!skb) { | ||
480 | /* invalid skb, no chance to proceed */ | ||
481 | printk(KERN_WARNING "isdn_v110_encode called with NULL skb!\n"); | ||
482 | return NULL; | ||
483 | } | ||
484 | rlen = skb->len; | ||
485 | nframes = (rlen + 3) / 4; | ||
486 | v110buf = v->encodebuf; | ||
487 | if ((nframes * 40) > v->maxsize) { | ||
488 | size = v->maxsize; | ||
489 | rlen = v->maxsize / 40; | ||
490 | } else | ||
491 | size = nframes * 40; | ||
492 | if (!(nskb = dev_alloc_skb(size + v->skbres + sizeof(int)))) { | ||
493 | printk(KERN_WARNING "isdn_v110_encode: Couldn't alloc skb\n"); | ||
494 | return NULL; | ||
495 | } | ||
496 | skb_reserve(nskb, v->skbres + sizeof(int)); | ||
497 | if (skb->len == 0) { | ||
498 | skb_put_data(nskb, v->OnlineFrame, v->framelen); | ||
499 | *((int *)skb_push(nskb, sizeof(int))) = 0; | ||
500 | return nskb; | ||
501 | } | ||
502 | mlen = EncodeMatrix(skb->data, rlen, v110buf, size); | ||
503 | /* now distribute 2 or 4 bits each to the output stream! */ | ||
504 | rbuf = skb_put(nskb, size); | ||
505 | olen = 0; | ||
506 | sval1 = 8 - v->nbits; | ||
507 | sval2 = v->key << sval1; | ||
508 | for (i = 0; i < mlen; i++) { | ||
509 | v110buf[i] = FlipBits(v110buf[i], v->nbits); | ||
510 | for (j = 0; j < v->nbytes; j++) { | ||
511 | if (size--) | ||
512 | *rbuf++ = ~v->key | (((v110buf[i] << (j * v->nbits)) & sval2) >> sval1); | ||
513 | else { | ||
514 | printk(KERN_WARNING "isdn_v110_encode: buffers full!\n"); | ||
515 | goto buffer_full; | ||
516 | } | ||
517 | olen++; | ||
518 | } | ||
519 | } | ||
520 | buffer_full: | ||
521 | skb_trim(nskb, olen); | ||
522 | *((int *)skb_push(nskb, sizeof(int))) = rlen; | ||
523 | return nskb; | ||
524 | } | ||
525 | |||
526 | int | ||
527 | isdn_v110_stat_callback(int idx, isdn_ctrl *c) | ||
528 | { | ||
529 | isdn_v110_stream *v = NULL; | ||
530 | int i; | ||
531 | int ret = 0; | ||
532 | |||
533 | if (idx < 0) | ||
534 | return 0; | ||
535 | switch (c->command) { | ||
536 | case ISDN_STAT_BSENT: | ||
537 | /* Keep the send-queue of the driver filled | ||
538 | * with frames: | ||
539 | * If number of outstanding frames < 3, | ||
540 | * send down an Idle-Frame (or an Sync-Frame, if | ||
541 | * v->SyncInit != 0). | ||
542 | */ | ||
543 | if (!(v = dev->v110[idx])) | ||
544 | return 0; | ||
545 | atomic_inc(&dev->v110use[idx]); | ||
546 | for (i = 0; i * v->framelen < c->parm.length; i++) { | ||
547 | if (v->skbidle > 0) { | ||
548 | v->skbidle--; | ||
549 | ret = 1; | ||
550 | } else { | ||
551 | if (v->skbuser > 0) | ||
552 | v->skbuser--; | ||
553 | ret = 0; | ||
554 | } | ||
555 | } | ||
556 | for (i = v->skbuser + v->skbidle; i < 2; i++) { | ||
557 | struct sk_buff *skb; | ||
558 | if (v->SyncInit > 0) | ||
559 | skb = isdn_v110_sync(v); | ||
560 | else | ||
561 | skb = isdn_v110_idle(v); | ||
562 | if (skb) { | ||
563 | if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) { | ||
564 | dev_kfree_skb(skb); | ||
565 | break; | ||
566 | } else { | ||
567 | if (v->SyncInit) | ||
568 | v->SyncInit--; | ||
569 | v->skbidle++; | ||
570 | } | ||
571 | } else | ||
572 | break; | ||
573 | } | ||
574 | atomic_dec(&dev->v110use[idx]); | ||
575 | return ret; | ||
576 | case ISDN_STAT_DHUP: | ||
577 | case ISDN_STAT_BHUP: | ||
578 | while (1) { | ||
579 | atomic_inc(&dev->v110use[idx]); | ||
580 | if (atomic_dec_and_test(&dev->v110use[idx])) { | ||
581 | isdn_v110_close(dev->v110[idx]); | ||
582 | dev->v110[idx] = NULL; | ||
583 | break; | ||
584 | } | ||
585 | mdelay(1); | ||
586 | } | ||
587 | break; | ||
588 | case ISDN_STAT_BCONN: | ||
589 | if (dev->v110emu[idx] && (dev->v110[idx] == NULL)) { | ||
590 | int hdrlen = dev->drv[c->driver]->interface->hl_hdrlen; | ||
591 | int maxsize = dev->drv[c->driver]->interface->maxbufsize; | ||
592 | atomic_inc(&dev->v110use[idx]); | ||
593 | switch (dev->v110emu[idx]) { | ||
594 | case ISDN_PROTO_L2_V11096: | ||
595 | dev->v110[idx] = isdn_v110_open(V110_9600, hdrlen, maxsize); | ||
596 | break; | ||
597 | case ISDN_PROTO_L2_V11019: | ||
598 | dev->v110[idx] = isdn_v110_open(V110_19200, hdrlen, maxsize); | ||
599 | break; | ||
600 | case ISDN_PROTO_L2_V11038: | ||
601 | dev->v110[idx] = isdn_v110_open(V110_38400, hdrlen, maxsize); | ||
602 | break; | ||
603 | default:; | ||
604 | } | ||
605 | if ((v = dev->v110[idx])) { | ||
606 | while (v->SyncInit) { | ||
607 | struct sk_buff *skb = isdn_v110_sync(v); | ||
608 | if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) { | ||
609 | dev_kfree_skb(skb); | ||
610 | /* Unable to send, try later */ | ||
611 | break; | ||
612 | } | ||
613 | v->SyncInit--; | ||
614 | v->skbidle++; | ||
615 | } | ||
616 | } else | ||
617 | printk(KERN_WARNING "isdn_v110: Couldn't open stream for chan %d\n", idx); | ||
618 | atomic_dec(&dev->v110use[idx]); | ||
619 | } | ||
620 | break; | ||
621 | default: | ||
622 | return 0; | ||
623 | } | ||
624 | return 0; | ||
625 | } | ||
diff --git a/drivers/isdn/i4l/isdn_v110.h b/drivers/isdn/i4l/isdn_v110.h deleted file mode 100644 index de774ab598c9..000000000000 --- a/drivers/isdn/i4l/isdn_v110.h +++ /dev/null | |||
@@ -1,29 +0,0 @@ | |||
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 deleted file mode 100644 index 48bfbcb4a09d..000000000000 --- a/drivers/isdn/i4l/isdn_x25iface.c +++ /dev/null | |||
@@ -1,332 +0,0 @@ | |||
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 which 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/slab.h> | ||
24 | #include <linux/wanrouter.h> | ||
25 | #include <net/x25device.h> | ||
26 | #include "isdn_x25iface.h" | ||
27 | |||
28 | /* for debugging messages not to cause an oops when device pointer is NULL*/ | ||
29 | #define MY_DEVNAME(dev) ((dev) ? (dev)->name : "DEVICE UNSPECIFIED") | ||
30 | |||
31 | |||
32 | typedef struct isdn_x25iface_proto_data { | ||
33 | int magic; | ||
34 | enum wan_states state; | ||
35 | /* Private stuff, not to be accessed via proto_data. We provide the | ||
36 | other storage for the concap_proto instance here as well, | ||
37 | enabling us to allocate both with just one kmalloc(): */ | ||
38 | struct concap_proto priv; | ||
39 | } ix25_pdata_t; | ||
40 | |||
41 | |||
42 | |||
43 | /* is now in header file (extern): struct concap_proto * isdn_x25iface_proto_new(void); */ | ||
44 | static void isdn_x25iface_proto_del(struct concap_proto *); | ||
45 | static int isdn_x25iface_proto_close(struct concap_proto *); | ||
46 | static int isdn_x25iface_proto_restart(struct concap_proto *, | ||
47 | struct net_device *, | ||
48 | struct concap_device_ops *); | ||
49 | static int isdn_x25iface_xmit(struct concap_proto *, struct sk_buff *); | ||
50 | static int isdn_x25iface_receive(struct concap_proto *, struct sk_buff *); | ||
51 | static int isdn_x25iface_connect_ind(struct concap_proto *); | ||
52 | static int isdn_x25iface_disconn_ind(struct concap_proto *); | ||
53 | |||
54 | |||
55 | static struct concap_proto_ops ix25_pops = { | ||
56 | .proto_new = &isdn_x25iface_proto_new, | ||
57 | .proto_del = &isdn_x25iface_proto_del, | ||
58 | .restart = &isdn_x25iface_proto_restart, | ||
59 | .close = &isdn_x25iface_proto_close, | ||
60 | .encap_and_xmit = &isdn_x25iface_xmit, | ||
61 | .data_ind = &isdn_x25iface_receive, | ||
62 | .connect_ind = &isdn_x25iface_connect_ind, | ||
63 | .disconn_ind = &isdn_x25iface_disconn_ind | ||
64 | }; | ||
65 | |||
66 | /* error message helper function */ | ||
67 | static void illegal_state_warn(unsigned state, unsigned char firstbyte) | ||
68 | { | ||
69 | printk(KERN_WARNING "isdn_x25iface: firstbyte %x illegal in" | ||
70 | "current state %d\n", firstbyte, state); | ||
71 | } | ||
72 | |||
73 | /* check protocol data field for consistency */ | ||
74 | static int pdata_is_bad(ix25_pdata_t *pda) { | ||
75 | |||
76 | if (pda && pda->magic == ISDN_X25IFACE_MAGIC) return 0; | ||
77 | printk(KERN_WARNING | ||
78 | "isdn_x25iface_xxx: illegal pointer to proto data\n"); | ||
79 | return 1; | ||
80 | } | ||
81 | |||
82 | /* create a new x25 interface protocol instance | ||
83 | */ | ||
84 | struct concap_proto *isdn_x25iface_proto_new(void) | ||
85 | { | ||
86 | ix25_pdata_t *tmp = kmalloc(sizeof(ix25_pdata_t), GFP_KERNEL); | ||
87 | IX25DEBUG("isdn_x25iface_proto_new\n"); | ||
88 | if (tmp) { | ||
89 | tmp->magic = ISDN_X25IFACE_MAGIC; | ||
90 | tmp->state = WAN_UNCONFIGURED; | ||
91 | /* private data space used to hold the concap_proto data. | ||
92 | Only to be accessed via the returned pointer */ | ||
93 | spin_lock_init(&tmp->priv.lock); | ||
94 | tmp->priv.dops = NULL; | ||
95 | tmp->priv.net_dev = NULL; | ||
96 | tmp->priv.pops = &ix25_pops; | ||
97 | tmp->priv.flags = 0; | ||
98 | tmp->priv.proto_data = tmp; | ||
99 | return (&(tmp->priv)); | ||
100 | } | ||
101 | return NULL; | ||
102 | }; | ||
103 | |||
104 | /* close the x25iface encapsulation protocol | ||
105 | */ | ||
106 | static int isdn_x25iface_proto_close(struct concap_proto *cprot) { | ||
107 | |||
108 | ix25_pdata_t *tmp; | ||
109 | int ret = 0; | ||
110 | ulong flags; | ||
111 | |||
112 | if (!cprot) { | ||
113 | printk(KERN_ERR "isdn_x25iface_proto_close: " | ||
114 | "invalid concap_proto pointer\n"); | ||
115 | return -1; | ||
116 | } | ||
117 | IX25DEBUG("isdn_x25iface_proto_close %s \n", MY_DEVNAME(cprot->net_dev)); | ||
118 | spin_lock_irqsave(&cprot->lock, flags); | ||
119 | cprot->dops = NULL; | ||
120 | cprot->net_dev = NULL; | ||
121 | tmp = cprot->proto_data; | ||
122 | if (pdata_is_bad(tmp)) { | ||
123 | ret = -1; | ||
124 | } else { | ||
125 | tmp->state = WAN_UNCONFIGURED; | ||
126 | } | ||
127 | spin_unlock_irqrestore(&cprot->lock, flags); | ||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | /* Delete the x25iface encapsulation protocol instance | ||
132 | */ | ||
133 | static void isdn_x25iface_proto_del(struct concap_proto *cprot) { | ||
134 | |||
135 | ix25_pdata_t *tmp; | ||
136 | |||
137 | IX25DEBUG("isdn_x25iface_proto_del \n"); | ||
138 | if (!cprot) { | ||
139 | printk(KERN_ERR "isdn_x25iface_proto_del: " | ||
140 | "concap_proto pointer is NULL\n"); | ||
141 | return; | ||
142 | } | ||
143 | tmp = cprot->proto_data; | ||
144 | if (tmp == NULL) { | ||
145 | printk(KERN_ERR "isdn_x25iface_proto_del: inconsistent " | ||
146 | "proto_data pointer (maybe already deleted?)\n"); | ||
147 | return; | ||
148 | } | ||
149 | /* close if the protocol is still open */ | ||
150 | if (cprot->dops) isdn_x25iface_proto_close(cprot); | ||
151 | /* freeing the storage should be sufficient now. But some additional | ||
152 | settings might help to catch wild pointer bugs */ | ||
153 | tmp->magic = 0; | ||
154 | cprot->proto_data = NULL; | ||
155 | |||
156 | kfree(tmp); | ||
157 | return; | ||
158 | } | ||
159 | |||
160 | /* (re-)initialize the data structures for x25iface encapsulation | ||
161 | */ | ||
162 | static int isdn_x25iface_proto_restart(struct concap_proto *cprot, | ||
163 | struct net_device *ndev, | ||
164 | struct concap_device_ops *dops) | ||
165 | { | ||
166 | ix25_pdata_t *pda = cprot->proto_data; | ||
167 | ulong flags; | ||
168 | |||
169 | IX25DEBUG("isdn_x25iface_proto_restart %s \n", MY_DEVNAME(ndev)); | ||
170 | |||
171 | if (pdata_is_bad(pda)) return -1; | ||
172 | |||
173 | if (!(dops && dops->data_req && dops->connect_req | ||
174 | && dops->disconn_req)) { | ||
175 | printk(KERN_WARNING "isdn_x25iface_restart: required dops" | ||
176 | " missing\n"); | ||
177 | isdn_x25iface_proto_close(cprot); | ||
178 | return -1; | ||
179 | } | ||
180 | spin_lock_irqsave(&cprot->lock, flags); | ||
181 | cprot->net_dev = ndev; | ||
182 | cprot->pops = &ix25_pops; | ||
183 | cprot->dops = dops; | ||
184 | pda->state = WAN_DISCONNECTED; | ||
185 | spin_unlock_irqrestore(&cprot->lock, flags); | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | /* deliver a dl_data frame received from i4l HL driver to the network layer | ||
190 | */ | ||
191 | static int isdn_x25iface_receive(struct concap_proto *cprot, struct sk_buff *skb) | ||
192 | { | ||
193 | IX25DEBUG("isdn_x25iface_receive %s \n", MY_DEVNAME(cprot->net_dev)); | ||
194 | if (((ix25_pdata_t *)(cprot->proto_data)) | ||
195 | ->state == WAN_CONNECTED) { | ||
196 | if (skb_push(skb, 1)) { | ||
197 | skb->data[0] = X25_IFACE_DATA; | ||
198 | skb->protocol = x25_type_trans(skb, cprot->net_dev); | ||
199 | netif_rx(skb); | ||
200 | return 0; | ||
201 | } | ||
202 | } | ||
203 | printk(KERN_WARNING "isdn_x25iface_receive %s: not connected, skb dropped\n", MY_DEVNAME(cprot->net_dev)); | ||
204 | dev_kfree_skb(skb); | ||
205 | return -1; | ||
206 | } | ||
207 | |||
208 | /* a connection set up is indicated by lower layer | ||
209 | */ | ||
210 | static int isdn_x25iface_connect_ind(struct concap_proto *cprot) | ||
211 | { | ||
212 | struct sk_buff *skb; | ||
213 | enum wan_states *state_p | ||
214 | = &(((ix25_pdata_t *)(cprot->proto_data))->state); | ||
215 | IX25DEBUG("isdn_x25iface_connect_ind %s \n" | ||
216 | , MY_DEVNAME(cprot->net_dev)); | ||
217 | if (*state_p == WAN_UNCONFIGURED) { | ||
218 | printk(KERN_WARNING | ||
219 | "isdn_x25iface_connect_ind while unconfigured %s\n" | ||
220 | , MY_DEVNAME(cprot->net_dev)); | ||
221 | return -1; | ||
222 | } | ||
223 | *state_p = WAN_CONNECTED; | ||
224 | |||
225 | skb = dev_alloc_skb(1); | ||
226 | if (skb) { | ||
227 | skb_put_u8(skb, X25_IFACE_CONNECT); | ||
228 | skb->protocol = x25_type_trans(skb, cprot->net_dev); | ||
229 | netif_rx(skb); | ||
230 | return 0; | ||
231 | } else { | ||
232 | printk(KERN_WARNING "isdn_x25iface_connect_ind: " | ||
233 | " out of memory -- disconnecting\n"); | ||
234 | cprot->dops->disconn_req(cprot); | ||
235 | return -1; | ||
236 | } | ||
237 | } | ||
238 | |||
239 | /* a disconnect is indicated by lower layer | ||
240 | */ | ||
241 | static int isdn_x25iface_disconn_ind(struct concap_proto *cprot) | ||
242 | { | ||
243 | struct sk_buff *skb; | ||
244 | enum wan_states *state_p | ||
245 | = &(((ix25_pdata_t *)(cprot->proto_data))->state); | ||
246 | IX25DEBUG("isdn_x25iface_disconn_ind %s \n", MY_DEVNAME(cprot->net_dev)); | ||
247 | if (*state_p == WAN_UNCONFIGURED) { | ||
248 | printk(KERN_WARNING | ||
249 | "isdn_x25iface_disconn_ind while unconfigured\n"); | ||
250 | return -1; | ||
251 | } | ||
252 | if (!cprot->net_dev) return -1; | ||
253 | *state_p = WAN_DISCONNECTED; | ||
254 | skb = dev_alloc_skb(1); | ||
255 | if (skb) { | ||
256 | skb_put_u8(skb, X25_IFACE_DISCONNECT); | ||
257 | skb->protocol = x25_type_trans(skb, cprot->net_dev); | ||
258 | netif_rx(skb); | ||
259 | return 0; | ||
260 | } else { | ||
261 | printk(KERN_WARNING "isdn_x25iface_disconn_ind:" | ||
262 | " out of memory\n"); | ||
263 | return -1; | ||
264 | } | ||
265 | } | ||
266 | |||
267 | /* process a frame handed over to us from linux network layer. First byte | ||
268 | semantics as defined in Documentation/networking/x25-iface.txt | ||
269 | */ | ||
270 | static int isdn_x25iface_xmit(struct concap_proto *cprot, struct sk_buff *skb) | ||
271 | { | ||
272 | unsigned char firstbyte = skb->data[0]; | ||
273 | enum wan_states *state = &((ix25_pdata_t *)cprot->proto_data)->state; | ||
274 | int ret = 0; | ||
275 | IX25DEBUG("isdn_x25iface_xmit: %s first=%x state=%d\n", | ||
276 | MY_DEVNAME(cprot->net_dev), firstbyte, *state); | ||
277 | switch (firstbyte) { | ||
278 | case X25_IFACE_DATA: | ||
279 | if (*state == WAN_CONNECTED) { | ||
280 | skb_pull(skb, 1); | ||
281 | netif_trans_update(cprot->net_dev); | ||
282 | ret = (cprot->dops->data_req(cprot, skb)); | ||
283 | /* prepare for future retransmissions */ | ||
284 | if (ret) skb_push(skb, 1); | ||
285 | return ret; | ||
286 | } | ||
287 | illegal_state_warn(*state, firstbyte); | ||
288 | break; | ||
289 | case X25_IFACE_CONNECT: | ||
290 | if (*state == WAN_DISCONNECTED) { | ||
291 | *state = WAN_CONNECTING; | ||
292 | ret = cprot->dops->connect_req(cprot); | ||
293 | if (ret) { | ||
294 | /* reset state and notify upper layer about | ||
295 | * immidiatly failed attempts */ | ||
296 | isdn_x25iface_disconn_ind(cprot); | ||
297 | } | ||
298 | } else { | ||
299 | illegal_state_warn(*state, firstbyte); | ||
300 | } | ||
301 | break; | ||
302 | case X25_IFACE_DISCONNECT: | ||
303 | switch (*state) { | ||
304 | case WAN_DISCONNECTED: | ||
305 | /* Should not happen. However, give upper layer a | ||
306 | chance to recover from inconstistency but don't | ||
307 | trust the lower layer sending the disconn_confirm | ||
308 | when already disconnected */ | ||
309 | printk(KERN_WARNING "isdn_x25iface_xmit: disconnect " | ||
310 | " requested while disconnected\n"); | ||
311 | isdn_x25iface_disconn_ind(cprot); | ||
312 | break; /* prevent infinite loops */ | ||
313 | case WAN_CONNECTING: | ||
314 | case WAN_CONNECTED: | ||
315 | *state = WAN_DISCONNECTED; | ||
316 | cprot->dops->disconn_req(cprot); | ||
317 | break; | ||
318 | default: | ||
319 | illegal_state_warn(*state, firstbyte); | ||
320 | } | ||
321 | break; | ||
322 | case X25_IFACE_PARAMS: | ||
323 | printk(KERN_WARNING "isdn_x25iface_xmit: setting of lapb" | ||
324 | " options not yet supported\n"); | ||
325 | break; | ||
326 | default: | ||
327 | printk(KERN_WARNING "isdn_x25iface_xmit: frame with illegal" | ||
328 | " first byte %x ignored:\n", firstbyte); | ||
329 | } | ||
330 | dev_kfree_skb(skb); | ||
331 | return 0; | ||
332 | } | ||
diff --git a/drivers/isdn/i4l/isdn_x25iface.h b/drivers/isdn/i4l/isdn_x25iface.h deleted file mode 100644 index ca08e082cf7c..000000000000 --- a/drivers/isdn/i4l/isdn_x25iface.h +++ /dev/null | |||
@@ -1,30 +0,0 @@ | |||
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/isdn.h> | ||
23 | #include <linux/concap.h> | ||
24 | |||
25 | extern struct concap_proto_ops *isdn_x25iface_concap_proto_ops_pt; | ||
26 | extern struct concap_proto *isdn_x25iface_proto_new(void); | ||
27 | |||
28 | |||
29 | |||
30 | #endif | ||
diff --git a/drivers/isdn/isdnloop/Makefile b/drivers/isdn/isdnloop/Makefile deleted file mode 100644 index 5ff4c0e09768..000000000000 --- a/drivers/isdn/isdnloop/Makefile +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0-only | ||
2 | # Makefile for the isdnloop ISDN device driver | ||
3 | |||
4 | # Each configuration option enables a list of files. | ||
5 | |||
6 | obj-$(CONFIG_ISDN_DRV_LOOP) += isdnloop.o | ||
diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c deleted file mode 100644 index 755c6bbc9553..000000000000 --- a/drivers/isdn/isdnloop/isdnloop.c +++ /dev/null | |||
@@ -1,1528 +0,0 @@ | |||
1 | /* $Id: isdnloop.c,v 1.11.6.7 2001/11/11 19:54:31 kai Exp $ | ||
2 | * | ||
3 | * ISDN low-level module implementing a dummy loop driver. | ||
4 | * | ||
5 | * Copyright 1997 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 | #include <linux/module.h> | ||
13 | #include <linux/interrupt.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include "isdnloop.h" | ||
18 | |||
19 | static char *isdnloop_id = "loop0"; | ||
20 | |||
21 | MODULE_DESCRIPTION("ISDN4Linux: Pseudo Driver that simulates an ISDN card"); | ||
22 | MODULE_AUTHOR("Fritz Elfert"); | ||
23 | MODULE_LICENSE("GPL"); | ||
24 | module_param(isdnloop_id, charp, 0); | ||
25 | MODULE_PARM_DESC(isdnloop_id, "ID-String of first card"); | ||
26 | |||
27 | static int isdnloop_addcard(char *); | ||
28 | |||
29 | /* | ||
30 | * Free queue completely. | ||
31 | * | ||
32 | * Parameter: | ||
33 | * card = pointer to card struct | ||
34 | * channel = channel number | ||
35 | */ | ||
36 | static void | ||
37 | isdnloop_free_queue(isdnloop_card *card, int channel) | ||
38 | { | ||
39 | struct sk_buff_head *queue = &card->bqueue[channel]; | ||
40 | |||
41 | skb_queue_purge(queue); | ||
42 | card->sndcount[channel] = 0; | ||
43 | } | ||
44 | |||
45 | /* | ||
46 | * Send B-Channel data to another virtual card. | ||
47 | * This routine is called via timer-callback from isdnloop_pollbchan(). | ||
48 | * | ||
49 | * Parameter: | ||
50 | * card = pointer to card struct. | ||
51 | * ch = channel number (0-based) | ||
52 | */ | ||
53 | static void | ||
54 | isdnloop_bchan_send(isdnloop_card *card, int ch) | ||
55 | { | ||
56 | isdnloop_card *rcard = card->rcard[ch]; | ||
57 | int rch = card->rch[ch], len, ack; | ||
58 | struct sk_buff *skb; | ||
59 | isdn_ctrl cmd; | ||
60 | |||
61 | while (card->sndcount[ch]) { | ||
62 | skb = skb_dequeue(&card->bqueue[ch]); | ||
63 | if (skb) { | ||
64 | len = skb->len; | ||
65 | card->sndcount[ch] -= len; | ||
66 | ack = *(skb->head); /* used as scratch area */ | ||
67 | cmd.driver = card->myid; | ||
68 | cmd.arg = ch; | ||
69 | if (rcard) { | ||
70 | rcard->interface.rcvcallb_skb(rcard->myid, rch, skb); | ||
71 | } else { | ||
72 | printk(KERN_WARNING "isdnloop: no rcard, skb dropped\n"); | ||
73 | dev_kfree_skb(skb); | ||
74 | |||
75 | } | ||
76 | cmd.command = ISDN_STAT_BSENT; | ||
77 | cmd.parm.length = len; | ||
78 | card->interface.statcallb(&cmd); | ||
79 | } else | ||
80 | card->sndcount[ch] = 0; | ||
81 | } | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | * Send/Receive Data to/from the B-Channel. | ||
86 | * This routine is called via timer-callback. | ||
87 | * It schedules itself while any B-Channel is open. | ||
88 | * | ||
89 | * Parameter: | ||
90 | * data = pointer to card struct, set by kernel timer.data | ||
91 | */ | ||
92 | static void | ||
93 | isdnloop_pollbchan(struct timer_list *t) | ||
94 | { | ||
95 | isdnloop_card *card = from_timer(card, t, rb_timer); | ||
96 | unsigned long flags; | ||
97 | |||
98 | if (card->flags & ISDNLOOP_FLAGS_B1ACTIVE) | ||
99 | isdnloop_bchan_send(card, 0); | ||
100 | if (card->flags & ISDNLOOP_FLAGS_B2ACTIVE) | ||
101 | isdnloop_bchan_send(card, 1); | ||
102 | if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE)) { | ||
103 | /* schedule b-channel polling again */ | ||
104 | spin_lock_irqsave(&card->isdnloop_lock, flags); | ||
105 | card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD; | ||
106 | add_timer(&card->rb_timer); | ||
107 | card->flags |= ISDNLOOP_FLAGS_RBTIMER; | ||
108 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
109 | } else | ||
110 | card->flags &= ~ISDNLOOP_FLAGS_RBTIMER; | ||
111 | } | ||
112 | |||
113 | /* | ||
114 | * Parse ICN-type setup string and fill fields of setup-struct | ||
115 | * with parsed data. | ||
116 | * | ||
117 | * Parameter: | ||
118 | * setup = setup string, format: [caller-id],si1,si2,[called-id] | ||
119 | * cmd = pointer to struct to be filled. | ||
120 | */ | ||
121 | static void | ||
122 | isdnloop_parse_setup(char *setup, isdn_ctrl *cmd) | ||
123 | { | ||
124 | char *t = setup; | ||
125 | char *s = strchr(t, ','); | ||
126 | |||
127 | *s++ = '\0'; | ||
128 | strlcpy(cmd->parm.setup.phone, t, sizeof(cmd->parm.setup.phone)); | ||
129 | s = strchr(t = s, ','); | ||
130 | *s++ = '\0'; | ||
131 | if (!strlen(t)) | ||
132 | cmd->parm.setup.si1 = 0; | ||
133 | else | ||
134 | cmd->parm.setup.si1 = simple_strtoul(t, NULL, 10); | ||
135 | s = strchr(t = s, ','); | ||
136 | *s++ = '\0'; | ||
137 | if (!strlen(t)) | ||
138 | cmd->parm.setup.si2 = 0; | ||
139 | else | ||
140 | cmd->parm.setup.si2 = | ||
141 | simple_strtoul(t, NULL, 10); | ||
142 | strlcpy(cmd->parm.setup.eazmsn, s, sizeof(cmd->parm.setup.eazmsn)); | ||
143 | cmd->parm.setup.plan = 0; | ||
144 | cmd->parm.setup.screen = 0; | ||
145 | } | ||
146 | |||
147 | typedef struct isdnloop_stat { | ||
148 | char *statstr; | ||
149 | int command; | ||
150 | int action; | ||
151 | } isdnloop_stat; | ||
152 | /* *INDENT-OFF* */ | ||
153 | static isdnloop_stat isdnloop_stat_table[] = { | ||
154 | {"BCON_", ISDN_STAT_BCONN, 1}, /* B-Channel connected */ | ||
155 | {"BDIS_", ISDN_STAT_BHUP, 2}, /* B-Channel disconnected */ | ||
156 | {"DCON_", ISDN_STAT_DCONN, 0}, /* D-Channel connected */ | ||
157 | {"DDIS_", ISDN_STAT_DHUP, 0}, /* D-Channel disconnected */ | ||
158 | {"DCAL_I", ISDN_STAT_ICALL, 3}, /* Incoming call dialup-line */ | ||
159 | {"DSCA_I", ISDN_STAT_ICALL, 3}, /* Incoming call 1TR6-SPV */ | ||
160 | {"FCALL", ISDN_STAT_ICALL, 4}, /* Leased line connection up */ | ||
161 | {"CIF", ISDN_STAT_CINF, 5}, /* Charge-info, 1TR6-type */ | ||
162 | {"AOC", ISDN_STAT_CINF, 6}, /* Charge-info, DSS1-type */ | ||
163 | {"CAU", ISDN_STAT_CAUSE, 7}, /* Cause code */ | ||
164 | {"TEI OK", ISDN_STAT_RUN, 0}, /* Card connected to wallplug */ | ||
165 | {"E_L1: ACT FAIL", ISDN_STAT_BHUP, 8}, /* Layer-1 activation failed */ | ||
166 | {"E_L2: DATA LIN", ISDN_STAT_BHUP, 8}, /* Layer-2 data link lost */ | ||
167 | {"E_L1: ACTIVATION FAILED", | ||
168 | ISDN_STAT_BHUP, 8}, /* Layer-1 activation failed */ | ||
169 | {NULL, 0, -1} | ||
170 | }; | ||
171 | /* *INDENT-ON* */ | ||
172 | |||
173 | |||
174 | /* | ||
175 | * Parse Status message-strings from virtual card. | ||
176 | * Depending on status, call statcallb for sending messages to upper | ||
177 | * levels. Also set/reset B-Channel active-flags. | ||
178 | * | ||
179 | * Parameter: | ||
180 | * status = status string to parse. | ||
181 | * channel = channel where message comes from. | ||
182 | * card = card where message comes from. | ||
183 | */ | ||
184 | static void | ||
185 | isdnloop_parse_status(u_char *status, int channel, isdnloop_card *card) | ||
186 | { | ||
187 | isdnloop_stat *s = isdnloop_stat_table; | ||
188 | int action = -1; | ||
189 | isdn_ctrl cmd; | ||
190 | |||
191 | while (s->statstr) { | ||
192 | if (!strncmp(status, s->statstr, strlen(s->statstr))) { | ||
193 | cmd.command = s->command; | ||
194 | action = s->action; | ||
195 | break; | ||
196 | } | ||
197 | s++; | ||
198 | } | ||
199 | if (action == -1) | ||
200 | return; | ||
201 | cmd.driver = card->myid; | ||
202 | cmd.arg = channel; | ||
203 | switch (action) { | ||
204 | case 1: | ||
205 | /* BCON_x */ | ||
206 | card->flags |= (channel) ? | ||
207 | ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE; | ||
208 | break; | ||
209 | case 2: | ||
210 | /* BDIS_x */ | ||
211 | card->flags &= ~((channel) ? | ||
212 | ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE); | ||
213 | isdnloop_free_queue(card, channel); | ||
214 | break; | ||
215 | case 3: | ||
216 | /* DCAL_I and DSCA_I */ | ||
217 | isdnloop_parse_setup(status + 6, &cmd); | ||
218 | break; | ||
219 | case 4: | ||
220 | /* FCALL */ | ||
221 | sprintf(cmd.parm.setup.phone, "LEASED%d", card->myid); | ||
222 | sprintf(cmd.parm.setup.eazmsn, "%d", channel + 1); | ||
223 | cmd.parm.setup.si1 = 7; | ||
224 | cmd.parm.setup.si2 = 0; | ||
225 | cmd.parm.setup.plan = 0; | ||
226 | cmd.parm.setup.screen = 0; | ||
227 | break; | ||
228 | case 5: | ||
229 | /* CIF */ | ||
230 | strlcpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num)); | ||
231 | break; | ||
232 | case 6: | ||
233 | /* AOC */ | ||
234 | snprintf(cmd.parm.num, sizeof(cmd.parm.num), "%d", | ||
235 | (int) simple_strtoul(status + 7, NULL, 16)); | ||
236 | break; | ||
237 | case 7: | ||
238 | /* CAU */ | ||
239 | status += 3; | ||
240 | if (strlen(status) == 4) | ||
241 | snprintf(cmd.parm.num, sizeof(cmd.parm.num), "%s%c%c", | ||
242 | status + 2, *status, *(status + 1)); | ||
243 | else | ||
244 | strlcpy(cmd.parm.num, status + 1, sizeof(cmd.parm.num)); | ||
245 | break; | ||
246 | case 8: | ||
247 | /* Misc Errors on L1 and L2 */ | ||
248 | card->flags &= ~ISDNLOOP_FLAGS_B1ACTIVE; | ||
249 | isdnloop_free_queue(card, 0); | ||
250 | cmd.arg = 0; | ||
251 | cmd.driver = card->myid; | ||
252 | card->interface.statcallb(&cmd); | ||
253 | cmd.command = ISDN_STAT_DHUP; | ||
254 | cmd.arg = 0; | ||
255 | cmd.driver = card->myid; | ||
256 | card->interface.statcallb(&cmd); | ||
257 | cmd.command = ISDN_STAT_BHUP; | ||
258 | card->flags &= ~ISDNLOOP_FLAGS_B2ACTIVE; | ||
259 | isdnloop_free_queue(card, 1); | ||
260 | cmd.arg = 1; | ||
261 | cmd.driver = card->myid; | ||
262 | card->interface.statcallb(&cmd); | ||
263 | cmd.command = ISDN_STAT_DHUP; | ||
264 | cmd.arg = 1; | ||
265 | cmd.driver = card->myid; | ||
266 | break; | ||
267 | } | ||
268 | card->interface.statcallb(&cmd); | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * Store a cwcharacter into ringbuffer for reading from /dev/isdnctrl | ||
273 | * | ||
274 | * Parameter: | ||
275 | * card = pointer to card struct. | ||
276 | * c = char to store. | ||
277 | */ | ||
278 | static void | ||
279 | isdnloop_putmsg(isdnloop_card *card, unsigned char c) | ||
280 | { | ||
281 | ulong flags; | ||
282 | |||
283 | spin_lock_irqsave(&card->isdnloop_lock, flags); | ||
284 | *card->msg_buf_write++ = (c == 0xff) ? '\n' : c; | ||
285 | if (card->msg_buf_write == card->msg_buf_read) { | ||
286 | if (++card->msg_buf_read > card->msg_buf_end) | ||
287 | card->msg_buf_read = card->msg_buf; | ||
288 | } | ||
289 | if (card->msg_buf_write > card->msg_buf_end) | ||
290 | card->msg_buf_write = card->msg_buf; | ||
291 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
292 | } | ||
293 | |||
294 | /* | ||
295 | * Poll a virtual cards message queue. | ||
296 | * If there are new status-replies from the card, copy them to | ||
297 | * ringbuffer for reading on /dev/isdnctrl and call | ||
298 | * isdnloop_parse_status() for processing them. Watch for special | ||
299 | * Firmware bootmessage and parse it, to get the D-Channel protocol. | ||
300 | * If there are B-Channels open, initiate a timer-callback to | ||
301 | * isdnloop_pollbchan(). | ||
302 | * This routine is called periodically via timer interrupt. | ||
303 | * | ||
304 | * Parameter: | ||
305 | * data = pointer to card struct | ||
306 | */ | ||
307 | static void | ||
308 | isdnloop_polldchan(struct timer_list *t) | ||
309 | { | ||
310 | isdnloop_card *card = from_timer(card, t, st_timer); | ||
311 | struct sk_buff *skb; | ||
312 | int avail; | ||
313 | int left; | ||
314 | u_char c; | ||
315 | int ch; | ||
316 | unsigned long flags; | ||
317 | u_char *p; | ||
318 | isdn_ctrl cmd; | ||
319 | |||
320 | skb = skb_dequeue(&card->dqueue); | ||
321 | if (skb) | ||
322 | avail = skb->len; | ||
323 | else | ||
324 | avail = 0; | ||
325 | for (left = avail; left > 0; left--) { | ||
326 | c = *skb->data; | ||
327 | skb_pull(skb, 1); | ||
328 | isdnloop_putmsg(card, c); | ||
329 | card->imsg[card->iptr] = c; | ||
330 | if (card->iptr < 59) | ||
331 | card->iptr++; | ||
332 | if (!skb->len) { | ||
333 | avail++; | ||
334 | isdnloop_putmsg(card, '\n'); | ||
335 | card->imsg[card->iptr] = 0; | ||
336 | card->iptr = 0; | ||
337 | if (card->imsg[0] == '0' && card->imsg[1] >= '0' && | ||
338 | card->imsg[1] <= '2' && card->imsg[2] == ';') { | ||
339 | ch = (card->imsg[1] - '0') - 1; | ||
340 | p = &card->imsg[3]; | ||
341 | isdnloop_parse_status(p, ch, card); | ||
342 | } else { | ||
343 | p = card->imsg; | ||
344 | if (!strncmp(p, "DRV1.", 5)) { | ||
345 | printk(KERN_INFO "isdnloop: (%s) %s\n", CID, p); | ||
346 | if (!strncmp(p + 7, "TC", 2)) { | ||
347 | card->ptype = ISDN_PTYPE_1TR6; | ||
348 | card->interface.features |= ISDN_FEATURE_P_1TR6; | ||
349 | printk(KERN_INFO | ||
350 | "isdnloop: (%s) 1TR6-Protocol loaded and running\n", CID); | ||
351 | } | ||
352 | if (!strncmp(p + 7, "EC", 2)) { | ||
353 | card->ptype = ISDN_PTYPE_EURO; | ||
354 | card->interface.features |= ISDN_FEATURE_P_EURO; | ||
355 | printk(KERN_INFO | ||
356 | "isdnloop: (%s) Euro-Protocol loaded and running\n", CID); | ||
357 | } | ||
358 | continue; | ||
359 | |||
360 | } | ||
361 | } | ||
362 | } | ||
363 | } | ||
364 | if (avail) { | ||
365 | cmd.command = ISDN_STAT_STAVAIL; | ||
366 | cmd.driver = card->myid; | ||
367 | cmd.arg = avail; | ||
368 | card->interface.statcallb(&cmd); | ||
369 | } | ||
370 | if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE)) | ||
371 | if (!(card->flags & ISDNLOOP_FLAGS_RBTIMER)) { | ||
372 | /* schedule b-channel polling */ | ||
373 | card->flags |= ISDNLOOP_FLAGS_RBTIMER; | ||
374 | spin_lock_irqsave(&card->isdnloop_lock, flags); | ||
375 | del_timer(&card->rb_timer); | ||
376 | card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD; | ||
377 | add_timer(&card->rb_timer); | ||
378 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
379 | } | ||
380 | /* schedule again */ | ||
381 | spin_lock_irqsave(&card->isdnloop_lock, flags); | ||
382 | card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD; | ||
383 | add_timer(&card->st_timer); | ||
384 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
385 | } | ||
386 | |||
387 | /* | ||
388 | * Append a packet to the transmit buffer-queue. | ||
389 | * | ||
390 | * Parameter: | ||
391 | * channel = Number of B-channel | ||
392 | * skb = packet to send. | ||
393 | * card = pointer to card-struct | ||
394 | * Return: | ||
395 | * Number of bytes transferred, -E??? on error | ||
396 | */ | ||
397 | static int | ||
398 | isdnloop_sendbuf(int channel, struct sk_buff *skb, isdnloop_card *card) | ||
399 | { | ||
400 | int len = skb->len; | ||
401 | unsigned long flags; | ||
402 | struct sk_buff *nskb; | ||
403 | |||
404 | if (len > 4000) { | ||
405 | printk(KERN_WARNING | ||
406 | "isdnloop: Send packet too large\n"); | ||
407 | return -EINVAL; | ||
408 | } | ||
409 | if (len) { | ||
410 | if (!(card->flags & (channel ? ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE))) | ||
411 | return 0; | ||
412 | if (card->sndcount[channel] > ISDNLOOP_MAX_SQUEUE) | ||
413 | return 0; | ||
414 | spin_lock_irqsave(&card->isdnloop_lock, flags); | ||
415 | nskb = dev_alloc_skb(skb->len); | ||
416 | if (nskb) { | ||
417 | skb_copy_from_linear_data(skb, | ||
418 | skb_put(nskb, len), len); | ||
419 | skb_queue_tail(&card->bqueue[channel], nskb); | ||
420 | dev_kfree_skb(skb); | ||
421 | } else | ||
422 | len = 0; | ||
423 | card->sndcount[channel] += len; | ||
424 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
425 | } | ||
426 | return len; | ||
427 | } | ||
428 | |||
429 | /* | ||
430 | * Read the messages from the card's ringbuffer | ||
431 | * | ||
432 | * Parameter: | ||
433 | * buf = pointer to buffer. | ||
434 | * len = number of bytes to read. | ||
435 | * user = flag, 1: called from userlevel 0: called from kernel. | ||
436 | * card = pointer to card struct. | ||
437 | * Return: | ||
438 | * number of bytes actually transferred. | ||
439 | */ | ||
440 | static int | ||
441 | isdnloop_readstatus(u_char __user *buf, int len, isdnloop_card *card) | ||
442 | { | ||
443 | int count; | ||
444 | u_char __user *p; | ||
445 | |||
446 | for (p = buf, count = 0; count < len; p++, count++) { | ||
447 | if (card->msg_buf_read == card->msg_buf_write) | ||
448 | return count; | ||
449 | if (put_user(*card->msg_buf_read++, p)) | ||
450 | return -EFAULT; | ||
451 | if (card->msg_buf_read > card->msg_buf_end) | ||
452 | card->msg_buf_read = card->msg_buf; | ||
453 | } | ||
454 | return count; | ||
455 | } | ||
456 | |||
457 | /* | ||
458 | * Simulate a card's response by appending it to the cards | ||
459 | * message queue. | ||
460 | * | ||
461 | * Parameter: | ||
462 | * card = pointer to card struct. | ||
463 | * s = pointer to message-string. | ||
464 | * ch = channel: 0 = generic messages, 1 and 2 = D-channel messages. | ||
465 | * Return: | ||
466 | * 0 on success, 1 on memory squeeze. | ||
467 | */ | ||
468 | static int | ||
469 | isdnloop_fake(isdnloop_card *card, char *s, int ch) | ||
470 | { | ||
471 | struct sk_buff *skb; | ||
472 | int len = strlen(s) + ((ch >= 0) ? 3 : 0); | ||
473 | skb = dev_alloc_skb(len); | ||
474 | if (!skb) { | ||
475 | printk(KERN_WARNING "isdnloop: Out of memory in isdnloop_fake\n"); | ||
476 | return 1; | ||
477 | } | ||
478 | if (ch >= 0) | ||
479 | sprintf(skb_put(skb, 3), "%02d;", ch); | ||
480 | skb_put_data(skb, s, strlen(s)); | ||
481 | skb_queue_tail(&card->dqueue, skb); | ||
482 | return 0; | ||
483 | } | ||
484 | /* *INDENT-OFF* */ | ||
485 | static isdnloop_stat isdnloop_cmd_table[] = { | ||
486 | {"BCON_R", 0, 1}, /* B-Channel connect */ | ||
487 | {"BCON_I", 0, 17}, /* B-Channel connect ind */ | ||
488 | {"BDIS_R", 0, 2}, /* B-Channel disconnect */ | ||
489 | {"DDIS_R", 0, 3}, /* D-Channel disconnect */ | ||
490 | {"DCON_R", 0, 16}, /* D-Channel connect */ | ||
491 | {"DSCA_R", 0, 4}, /* Dial 1TR6-SPV */ | ||
492 | {"DCAL_R", 0, 5}, /* Dial */ | ||
493 | {"EAZC", 0, 6}, /* Clear EAZ listener */ | ||
494 | {"EAZ", 0, 7}, /* Set EAZ listener */ | ||
495 | {"SEEAZ", 0, 8}, /* Get EAZ listener */ | ||
496 | {"MSN", 0, 9}, /* Set/Clear MSN listener */ | ||
497 | {"MSALL", 0, 10}, /* Set multi MSN listeners */ | ||
498 | {"SETSIL", 0, 11}, /* Set SI list */ | ||
499 | {"SEESIL", 0, 12}, /* Get SI list */ | ||
500 | {"SILC", 0, 13}, /* Clear SI list */ | ||
501 | {"LOCK", 0, -1}, /* LOCK channel */ | ||
502 | {"UNLOCK", 0, -1}, /* UNLOCK channel */ | ||
503 | {"FV2ON", 1, 14}, /* Leased mode on */ | ||
504 | {"FV2OFF", 1, 15}, /* Leased mode off */ | ||
505 | {NULL, 0, -1} | ||
506 | }; | ||
507 | /* *INDENT-ON* */ | ||
508 | |||
509 | |||
510 | /* | ||
511 | * Simulate an error-response from a card. | ||
512 | * | ||
513 | * Parameter: | ||
514 | * card = pointer to card struct. | ||
515 | */ | ||
516 | static void | ||
517 | isdnloop_fake_err(isdnloop_card *card) | ||
518 | { | ||
519 | char buf[64]; | ||
520 | |||
521 | snprintf(buf, sizeof(buf), "E%s", card->omsg); | ||
522 | isdnloop_fake(card, buf, -1); | ||
523 | isdnloop_fake(card, "NAK", -1); | ||
524 | } | ||
525 | |||
526 | static u_char ctable_eu[] = {0x00, 0x11, 0x01, 0x12}; | ||
527 | static u_char ctable_1t[] = {0x00, 0x3b, 0x01, 0x3a}; | ||
528 | |||
529 | /* | ||
530 | * Assemble a simplified cause message depending on the | ||
531 | * D-channel protocol used. | ||
532 | * | ||
533 | * Parameter: | ||
534 | * card = pointer to card struct. | ||
535 | * loc = location: 0 = local, 1 = remote. | ||
536 | * cau = cause: 1 = busy, 2 = nonexistent callerid, 3 = no user responding. | ||
537 | * Return: | ||
538 | * Pointer to buffer containing the assembled message. | ||
539 | */ | ||
540 | static char * | ||
541 | isdnloop_unicause(isdnloop_card *card, int loc, int cau) | ||
542 | { | ||
543 | static char buf[6]; | ||
544 | |||
545 | switch (card->ptype) { | ||
546 | case ISDN_PTYPE_EURO: | ||
547 | sprintf(buf, "E%02X%02X", (loc) ? 4 : 2, ctable_eu[cau]); | ||
548 | break; | ||
549 | case ISDN_PTYPE_1TR6: | ||
550 | sprintf(buf, "%02X44", ctable_1t[cau]); | ||
551 | break; | ||
552 | default: | ||
553 | return "0000"; | ||
554 | } | ||
555 | return buf; | ||
556 | } | ||
557 | |||
558 | /* | ||
559 | * Release a virtual connection. Called from timer interrupt, when | ||
560 | * called party did not respond. | ||
561 | * | ||
562 | * Parameter: | ||
563 | * card = pointer to card struct. | ||
564 | * ch = channel (0-based) | ||
565 | */ | ||
566 | static void | ||
567 | isdnloop_atimeout(isdnloop_card *card, int ch) | ||
568 | { | ||
569 | unsigned long flags; | ||
570 | char buf[60]; | ||
571 | |||
572 | spin_lock_irqsave(&card->isdnloop_lock, flags); | ||
573 | if (card->rcard[ch]) { | ||
574 | isdnloop_fake(card->rcard[ch], "DDIS_I", card->rch[ch] + 1); | ||
575 | card->rcard[ch]->rcard[card->rch[ch]] = NULL; | ||
576 | card->rcard[ch] = NULL; | ||
577 | } | ||
578 | isdnloop_fake(card, "DDIS_I", ch + 1); | ||
579 | /* No user responding */ | ||
580 | sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 3)); | ||
581 | isdnloop_fake(card, buf, ch + 1); | ||
582 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
583 | } | ||
584 | |||
585 | /* | ||
586 | * Wrapper for isdnloop_atimeout(). | ||
587 | */ | ||
588 | static void | ||
589 | isdnloop_atimeout0(struct timer_list *t) | ||
590 | { | ||
591 | isdnloop_card *card = from_timer(card, t, c_timer[0]); | ||
592 | |||
593 | isdnloop_atimeout(card, 0); | ||
594 | } | ||
595 | |||
596 | /* | ||
597 | * Wrapper for isdnloop_atimeout(). | ||
598 | */ | ||
599 | static void | ||
600 | isdnloop_atimeout1(struct timer_list *t) | ||
601 | { | ||
602 | isdnloop_card *card = from_timer(card, t, c_timer[1]); | ||
603 | |||
604 | isdnloop_atimeout(card, 1); | ||
605 | } | ||
606 | |||
607 | /* | ||
608 | * Install a watchdog for a user, not responding. | ||
609 | * | ||
610 | * Parameter: | ||
611 | * card = pointer to card struct. | ||
612 | * ch = channel to watch for. | ||
613 | */ | ||
614 | static void | ||
615 | isdnloop_start_ctimer(isdnloop_card *card, int ch) | ||
616 | { | ||
617 | unsigned long flags; | ||
618 | |||
619 | spin_lock_irqsave(&card->isdnloop_lock, flags); | ||
620 | timer_setup(&card->c_timer[ch], ch ? isdnloop_atimeout1 | ||
621 | : isdnloop_atimeout0, 0); | ||
622 | card->c_timer[ch].expires = jiffies + ISDNLOOP_TIMER_ALERTWAIT; | ||
623 | add_timer(&card->c_timer[ch]); | ||
624 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
625 | } | ||
626 | |||
627 | /* | ||
628 | * Kill a pending channel watchdog. | ||
629 | * | ||
630 | * Parameter: | ||
631 | * card = pointer to card struct. | ||
632 | * ch = channel (0-based). | ||
633 | */ | ||
634 | static void | ||
635 | isdnloop_kill_ctimer(isdnloop_card *card, int ch) | ||
636 | { | ||
637 | unsigned long flags; | ||
638 | |||
639 | spin_lock_irqsave(&card->isdnloop_lock, flags); | ||
640 | del_timer(&card->c_timer[ch]); | ||
641 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
642 | } | ||
643 | |||
644 | static u_char si2bit[] = {0, 1, 0, 0, 0, 2, 0, 4, 0, 0}; | ||
645 | static u_char bit2si[] = {1, 5, 7}; | ||
646 | |||
647 | /* | ||
648 | * Try finding a listener for an outgoing call. | ||
649 | * | ||
650 | * Parameter: | ||
651 | * card = pointer to calling card. | ||
652 | * p = pointer to ICN-type setup-string. | ||
653 | * lch = channel of calling card. | ||
654 | * cmd = pointer to struct to be filled when parsing setup. | ||
655 | * Return: | ||
656 | * 0 = found match, alerting should happen. | ||
657 | * 1 = found matching number but it is busy. | ||
658 | * 2 = no matching listener. | ||
659 | * 3 = found matching number but SI does not match. | ||
660 | */ | ||
661 | static int | ||
662 | isdnloop_try_call(isdnloop_card *card, char *p, int lch, isdn_ctrl *cmd) | ||
663 | { | ||
664 | isdnloop_card *cc = cards; | ||
665 | unsigned long flags; | ||
666 | int ch; | ||
667 | int num_match; | ||
668 | int i; | ||
669 | char *e; | ||
670 | char nbuf[32]; | ||
671 | |||
672 | isdnloop_parse_setup(p, cmd); | ||
673 | while (cc) { | ||
674 | for (ch = 0; ch < 2; ch++) { | ||
675 | /* Exclude ourself */ | ||
676 | if ((cc == card) && (ch == lch)) | ||
677 | continue; | ||
678 | num_match = 0; | ||
679 | switch (cc->ptype) { | ||
680 | case ISDN_PTYPE_EURO: | ||
681 | for (i = 0; i < 3; i++) | ||
682 | if (!(strcmp(cc->s0num[i], cmd->parm.setup.phone))) | ||
683 | num_match = 1; | ||
684 | break; | ||
685 | case ISDN_PTYPE_1TR6: | ||
686 | e = cc->eazlist[ch]; | ||
687 | while (*e) { | ||
688 | sprintf(nbuf, "%s%c", cc->s0num[0], *e); | ||
689 | if (!(strcmp(nbuf, cmd->parm.setup.phone))) | ||
690 | num_match = 1; | ||
691 | e++; | ||
692 | } | ||
693 | } | ||
694 | if (num_match) { | ||
695 | spin_lock_irqsave(&card->isdnloop_lock, flags); | ||
696 | /* channel idle? */ | ||
697 | if (!(cc->rcard[ch])) { | ||
698 | /* Check SI */ | ||
699 | if (!(si2bit[cmd->parm.setup.si1] & cc->sil[ch])) { | ||
700 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
701 | return 3; | ||
702 | } | ||
703 | /* ch is idle, si and number matches */ | ||
704 | cc->rcard[ch] = card; | ||
705 | cc->rch[ch] = lch; | ||
706 | card->rcard[lch] = cc; | ||
707 | card->rch[lch] = ch; | ||
708 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
709 | return 0; | ||
710 | } else { | ||
711 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
712 | /* num matches, but busy */ | ||
713 | if (ch == 1) | ||
714 | return 1; | ||
715 | } | ||
716 | } | ||
717 | } | ||
718 | cc = cc->next; | ||
719 | } | ||
720 | return 2; | ||
721 | } | ||
722 | |||
723 | /* | ||
724 | * Depending on D-channel protocol and caller/called, modify | ||
725 | * phone number. | ||
726 | * | ||
727 | * Parameter: | ||
728 | * card = pointer to card struct. | ||
729 | * phone = pointer phone number. | ||
730 | * caller = flag: 1 = caller, 0 = called. | ||
731 | * Return: | ||
732 | * pointer to new phone number. | ||
733 | */ | ||
734 | static char * | ||
735 | isdnloop_vstphone(isdnloop_card *card, char *phone, int caller) | ||
736 | { | ||
737 | int i; | ||
738 | static char nphone[30]; | ||
739 | |||
740 | if (!card) { | ||
741 | printk("BUG!!!\n"); | ||
742 | return ""; | ||
743 | } | ||
744 | switch (card->ptype) { | ||
745 | case ISDN_PTYPE_EURO: | ||
746 | if (caller) { | ||
747 | for (i = 0; i < 2; i++) | ||
748 | if (!(strcmp(card->s0num[i], phone))) | ||
749 | return phone; | ||
750 | return card->s0num[0]; | ||
751 | } | ||
752 | return phone; | ||
753 | break; | ||
754 | case ISDN_PTYPE_1TR6: | ||
755 | if (caller) { | ||
756 | sprintf(nphone, "%s%c", card->s0num[0], phone[0]); | ||
757 | return nphone; | ||
758 | } else | ||
759 | return &phone[strlen(phone) - 1]; | ||
760 | break; | ||
761 | } | ||
762 | return ""; | ||
763 | } | ||
764 | |||
765 | /* | ||
766 | * Parse an ICN-type command string sent to the 'card'. | ||
767 | * Perform misc. actions depending on the command. | ||
768 | * | ||
769 | * Parameter: | ||
770 | * card = pointer to card struct. | ||
771 | */ | ||
772 | static void | ||
773 | isdnloop_parse_cmd(isdnloop_card *card) | ||
774 | { | ||
775 | char *p = card->omsg; | ||
776 | isdn_ctrl cmd; | ||
777 | char buf[60]; | ||
778 | isdnloop_stat *s = isdnloop_cmd_table; | ||
779 | int action = -1; | ||
780 | int i; | ||
781 | int ch; | ||
782 | |||
783 | if ((card->omsg[0] != '0') && (card->omsg[2] != ';')) { | ||
784 | isdnloop_fake_err(card); | ||
785 | return; | ||
786 | } | ||
787 | ch = card->omsg[1] - '0'; | ||
788 | if ((ch < 0) || (ch > 2)) { | ||
789 | isdnloop_fake_err(card); | ||
790 | return; | ||
791 | } | ||
792 | p += 3; | ||
793 | while (s->statstr) { | ||
794 | if (!strncmp(p, s->statstr, strlen(s->statstr))) { | ||
795 | action = s->action; | ||
796 | if (s->command && (ch != 0)) { | ||
797 | isdnloop_fake_err(card); | ||
798 | return; | ||
799 | } | ||
800 | break; | ||
801 | } | ||
802 | s++; | ||
803 | } | ||
804 | if (action == -1) | ||
805 | return; | ||
806 | switch (action) { | ||
807 | case 1: | ||
808 | /* 0x;BCON_R */ | ||
809 | if (card->rcard[ch - 1]) { | ||
810 | isdnloop_fake(card->rcard[ch - 1], "BCON_I", | ||
811 | card->rch[ch - 1] + 1); | ||
812 | isdnloop_fake(card, "BCON_C", ch); | ||
813 | } | ||
814 | break; | ||
815 | case 17: | ||
816 | /* 0x;BCON_I */ | ||
817 | if (card->rcard[ch - 1]) { | ||
818 | isdnloop_fake(card->rcard[ch - 1], "BCON_C", | ||
819 | card->rch[ch - 1] + 1); | ||
820 | } | ||
821 | break; | ||
822 | case 2: | ||
823 | /* 0x;BDIS_R */ | ||
824 | isdnloop_fake(card, "BDIS_C", ch); | ||
825 | if (card->rcard[ch - 1]) { | ||
826 | isdnloop_fake(card->rcard[ch - 1], "BDIS_I", | ||
827 | card->rch[ch - 1] + 1); | ||
828 | } | ||
829 | break; | ||
830 | case 16: | ||
831 | /* 0x;DCON_R */ | ||
832 | isdnloop_kill_ctimer(card, ch - 1); | ||
833 | if (card->rcard[ch - 1]) { | ||
834 | isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]); | ||
835 | isdnloop_fake(card->rcard[ch - 1], "DCON_C", | ||
836 | card->rch[ch - 1] + 1); | ||
837 | isdnloop_fake(card, "DCON_C", ch); | ||
838 | } | ||
839 | break; | ||
840 | case 3: | ||
841 | /* 0x;DDIS_R */ | ||
842 | isdnloop_kill_ctimer(card, ch - 1); | ||
843 | if (card->rcard[ch - 1]) { | ||
844 | isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]); | ||
845 | isdnloop_fake(card->rcard[ch - 1], "DDIS_I", | ||
846 | card->rch[ch - 1] + 1); | ||
847 | card->rcard[ch - 1] = NULL; | ||
848 | } | ||
849 | isdnloop_fake(card, "DDIS_C", ch); | ||
850 | break; | ||
851 | case 4: | ||
852 | /* 0x;DSCA_Rdd,yy,zz,oo */ | ||
853 | if (card->ptype != ISDN_PTYPE_1TR6) { | ||
854 | isdnloop_fake_err(card); | ||
855 | return; | ||
856 | } | ||
857 | /* Fall through */ | ||
858 | case 5: | ||
859 | /* 0x;DCAL_Rdd,yy,zz,oo */ | ||
860 | p += 6; | ||
861 | switch (isdnloop_try_call(card, p, ch - 1, &cmd)) { | ||
862 | case 0: | ||
863 | /* Alerting */ | ||
864 | sprintf(buf, "D%s_I%s,%02d,%02d,%s", | ||
865 | (action == 4) ? "SCA" : "CAL", | ||
866 | isdnloop_vstphone(card, cmd.parm.setup.eazmsn, 1), | ||
867 | cmd.parm.setup.si1, | ||
868 | cmd.parm.setup.si2, | ||
869 | isdnloop_vstphone(card->rcard[ch - 1], | ||
870 | cmd.parm.setup.phone, 0)); | ||
871 | isdnloop_fake(card->rcard[ch - 1], buf, card->rch[ch - 1] + 1); | ||
872 | /* Fall through */ | ||
873 | case 3: | ||
874 | /* si1 does not match, don't alert but start timer */ | ||
875 | isdnloop_start_ctimer(card, ch - 1); | ||
876 | break; | ||
877 | case 1: | ||
878 | /* Remote busy */ | ||
879 | isdnloop_fake(card, "DDIS_I", ch); | ||
880 | sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 1)); | ||
881 | isdnloop_fake(card, buf, ch); | ||
882 | break; | ||
883 | case 2: | ||
884 | /* No such user */ | ||
885 | isdnloop_fake(card, "DDIS_I", ch); | ||
886 | sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 2)); | ||
887 | isdnloop_fake(card, buf, ch); | ||
888 | break; | ||
889 | } | ||
890 | break; | ||
891 | case 6: | ||
892 | /* 0x;EAZC */ | ||
893 | card->eazlist[ch - 1][0] = '\0'; | ||
894 | break; | ||
895 | case 7: | ||
896 | /* 0x;EAZ */ | ||
897 | p += 3; | ||
898 | if (strlen(p) >= sizeof(card->eazlist[0])) | ||
899 | break; | ||
900 | strcpy(card->eazlist[ch - 1], p); | ||
901 | break; | ||
902 | case 8: | ||
903 | /* 0x;SEEAZ */ | ||
904 | sprintf(buf, "EAZ-LIST: %s", card->eazlist[ch - 1]); | ||
905 | isdnloop_fake(card, buf, ch + 1); | ||
906 | break; | ||
907 | case 9: | ||
908 | /* 0x;MSN */ | ||
909 | break; | ||
910 | case 10: | ||
911 | /* 0x;MSNALL */ | ||
912 | break; | ||
913 | case 11: | ||
914 | /* 0x;SETSIL */ | ||
915 | p += 6; | ||
916 | i = 0; | ||
917 | while (strchr("0157", *p)) { | ||
918 | if (i) | ||
919 | card->sil[ch - 1] |= si2bit[*p - '0']; | ||
920 | i = (*p++ == '0'); | ||
921 | } | ||
922 | if (*p) | ||
923 | isdnloop_fake_err(card); | ||
924 | break; | ||
925 | case 12: | ||
926 | /* 0x;SEESIL */ | ||
927 | sprintf(buf, "SIN-LIST: "); | ||
928 | p = buf + 10; | ||
929 | for (i = 0; i < 3; i++) | ||
930 | if (card->sil[ch - 1] & (1 << i)) | ||
931 | p += sprintf(p, "%02d", bit2si[i]); | ||
932 | isdnloop_fake(card, buf, ch + 1); | ||
933 | break; | ||
934 | case 13: | ||
935 | /* 0x;SILC */ | ||
936 | card->sil[ch - 1] = 0; | ||
937 | break; | ||
938 | case 14: | ||
939 | /* 00;FV2ON */ | ||
940 | break; | ||
941 | case 15: | ||
942 | /* 00;FV2OFF */ | ||
943 | break; | ||
944 | } | ||
945 | } | ||
946 | |||
947 | /* | ||
948 | * Put command-strings into the of the 'card'. In reality, execute them | ||
949 | * right in place by calling isdnloop_parse_cmd(). Also copy every | ||
950 | * command to the read message ringbuffer, preceding it with a '>'. | ||
951 | * These mesagges can be read at /dev/isdnctrl. | ||
952 | * | ||
953 | * Parameter: | ||
954 | * buf = pointer to command buffer. | ||
955 | * len = length of buffer data. | ||
956 | * user = flag: 1 = called form userlevel, 0 called from kernel. | ||
957 | * card = pointer to card struct. | ||
958 | * Return: | ||
959 | * number of bytes transferred (currently always equals len). | ||
960 | */ | ||
961 | static int | ||
962 | isdnloop_writecmd(const u_char *buf, int len, int user, isdnloop_card *card) | ||
963 | { | ||
964 | int xcount = 0; | ||
965 | int ocount = 1; | ||
966 | isdn_ctrl cmd; | ||
967 | |||
968 | while (len) { | ||
969 | int count = len; | ||
970 | u_char *p; | ||
971 | u_char msg[0x100]; | ||
972 | |||
973 | if (count > 255) | ||
974 | count = 255; | ||
975 | if (user) { | ||
976 | if (copy_from_user(msg, buf, count)) | ||
977 | return -EFAULT; | ||
978 | } else | ||
979 | memcpy(msg, buf, count); | ||
980 | isdnloop_putmsg(card, '>'); | ||
981 | for (p = msg; count > 0; count--, p++) { | ||
982 | len--; | ||
983 | xcount++; | ||
984 | isdnloop_putmsg(card, *p); | ||
985 | card->omsg[card->optr] = *p; | ||
986 | if (*p == '\n') { | ||
987 | card->omsg[card->optr] = '\0'; | ||
988 | card->optr = 0; | ||
989 | isdnloop_parse_cmd(card); | ||
990 | if (len) { | ||
991 | isdnloop_putmsg(card, '>'); | ||
992 | ocount++; | ||
993 | } | ||
994 | } else { | ||
995 | if (card->optr < 59) | ||
996 | card->optr++; | ||
997 | } | ||
998 | ocount++; | ||
999 | } | ||
1000 | } | ||
1001 | cmd.command = ISDN_STAT_STAVAIL; | ||
1002 | cmd.driver = card->myid; | ||
1003 | cmd.arg = ocount; | ||
1004 | card->interface.statcallb(&cmd); | ||
1005 | return xcount; | ||
1006 | } | ||
1007 | |||
1008 | /* | ||
1009 | * Delete card's pending timers, send STOP to linklevel | ||
1010 | */ | ||
1011 | static void | ||
1012 | isdnloop_stopcard(isdnloop_card *card) | ||
1013 | { | ||
1014 | unsigned long flags; | ||
1015 | isdn_ctrl cmd; | ||
1016 | |||
1017 | spin_lock_irqsave(&card->isdnloop_lock, flags); | ||
1018 | if (card->flags & ISDNLOOP_FLAGS_RUNNING) { | ||
1019 | card->flags &= ~ISDNLOOP_FLAGS_RUNNING; | ||
1020 | del_timer(&card->st_timer); | ||
1021 | del_timer(&card->rb_timer); | ||
1022 | del_timer(&card->c_timer[0]); | ||
1023 | del_timer(&card->c_timer[1]); | ||
1024 | cmd.command = ISDN_STAT_STOP; | ||
1025 | cmd.driver = card->myid; | ||
1026 | card->interface.statcallb(&cmd); | ||
1027 | } | ||
1028 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
1029 | } | ||
1030 | |||
1031 | /* | ||
1032 | * Stop all cards before unload. | ||
1033 | */ | ||
1034 | static void | ||
1035 | isdnloop_stopallcards(void) | ||
1036 | { | ||
1037 | isdnloop_card *p = cards; | ||
1038 | |||
1039 | while (p) { | ||
1040 | isdnloop_stopcard(p); | ||
1041 | p = p->next; | ||
1042 | } | ||
1043 | } | ||
1044 | |||
1045 | /* | ||
1046 | * Start a 'card'. Simulate card's boot message and set the phone | ||
1047 | * number(s) of the virtual 'S0-Interface'. Install D-channel | ||
1048 | * poll timer. | ||
1049 | * | ||
1050 | * Parameter: | ||
1051 | * card = pointer to card struct. | ||
1052 | * sdefp = pointer to struct holding ioctl parameters. | ||
1053 | * Return: | ||
1054 | * 0 on success, -E??? otherwise. | ||
1055 | */ | ||
1056 | static int | ||
1057 | isdnloop_start(isdnloop_card *card, isdnloop_sdef *sdefp) | ||
1058 | { | ||
1059 | unsigned long flags; | ||
1060 | isdnloop_sdef sdef; | ||
1061 | int i; | ||
1062 | |||
1063 | if (card->flags & ISDNLOOP_FLAGS_RUNNING) | ||
1064 | return -EBUSY; | ||
1065 | if (copy_from_user((char *) &sdef, (char *) sdefp, sizeof(sdef))) | ||
1066 | return -EFAULT; | ||
1067 | |||
1068 | for (i = 0; i < 3; i++) { | ||
1069 | if (!memchr(sdef.num[i], 0, sizeof(sdef.num[i]))) | ||
1070 | return -EINVAL; | ||
1071 | } | ||
1072 | |||
1073 | spin_lock_irqsave(&card->isdnloop_lock, flags); | ||
1074 | switch (sdef.ptype) { | ||
1075 | case ISDN_PTYPE_EURO: | ||
1076 | if (isdnloop_fake(card, "DRV1.23EC-Q.931-CAPI-CNS-BASIS-20.02.96", | ||
1077 | -1)) { | ||
1078 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
1079 | return -ENOMEM; | ||
1080 | } | ||
1081 | card->sil[0] = card->sil[1] = 4; | ||
1082 | if (isdnloop_fake(card, "TEI OK", 0)) { | ||
1083 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
1084 | return -ENOMEM; | ||
1085 | } | ||
1086 | for (i = 0; i < 3; i++) { | ||
1087 | strlcpy(card->s0num[i], sdef.num[i], | ||
1088 | sizeof(card->s0num[0])); | ||
1089 | } | ||
1090 | break; | ||
1091 | case ISDN_PTYPE_1TR6: | ||
1092 | if (isdnloop_fake(card, "DRV1.04TC-1TR6-CAPI-CNS-BASIS-29.11.95", | ||
1093 | -1)) { | ||
1094 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
1095 | return -ENOMEM; | ||
1096 | } | ||
1097 | card->sil[0] = card->sil[1] = 4; | ||
1098 | if (isdnloop_fake(card, "TEI OK", 0)) { | ||
1099 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
1100 | return -ENOMEM; | ||
1101 | } | ||
1102 | strlcpy(card->s0num[0], sdef.num[0], sizeof(card->s0num[0])); | ||
1103 | card->s0num[1][0] = '\0'; | ||
1104 | card->s0num[2][0] = '\0'; | ||
1105 | break; | ||
1106 | default: | ||
1107 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
1108 | printk(KERN_WARNING "isdnloop: Illegal D-channel protocol %d\n", | ||
1109 | sdef.ptype); | ||
1110 | return -EINVAL; | ||
1111 | } | ||
1112 | timer_setup(&card->rb_timer, isdnloop_pollbchan, 0); | ||
1113 | timer_setup(&card->st_timer, isdnloop_polldchan, 0); | ||
1114 | card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD; | ||
1115 | add_timer(&card->st_timer); | ||
1116 | card->flags |= ISDNLOOP_FLAGS_RUNNING; | ||
1117 | spin_unlock_irqrestore(&card->isdnloop_lock, flags); | ||
1118 | return 0; | ||
1119 | } | ||
1120 | |||
1121 | /* | ||
1122 | * Main handler for commands sent by linklevel. | ||
1123 | */ | ||
1124 | static int | ||
1125 | isdnloop_command(isdn_ctrl *c, isdnloop_card *card) | ||
1126 | { | ||
1127 | ulong a; | ||
1128 | int i; | ||
1129 | char cbuf[80]; | ||
1130 | isdn_ctrl cmd; | ||
1131 | isdnloop_cdef cdef; | ||
1132 | |||
1133 | switch (c->command) { | ||
1134 | case ISDN_CMD_IOCTL: | ||
1135 | memcpy(&a, c->parm.num, sizeof(ulong)); | ||
1136 | switch (c->arg) { | ||
1137 | case ISDNLOOP_IOCTL_DEBUGVAR: | ||
1138 | return (ulong) card; | ||
1139 | case ISDNLOOP_IOCTL_STARTUP: | ||
1140 | return isdnloop_start(card, (isdnloop_sdef *) a); | ||
1141 | break; | ||
1142 | case ISDNLOOP_IOCTL_ADDCARD: | ||
1143 | if (copy_from_user((char *)&cdef, | ||
1144 | (char *)a, | ||
1145 | sizeof(cdef))) | ||
1146 | return -EFAULT; | ||
1147 | return isdnloop_addcard(cdef.id1); | ||
1148 | break; | ||
1149 | case ISDNLOOP_IOCTL_LEASEDCFG: | ||
1150 | if (a) { | ||
1151 | if (!card->leased) { | ||
1152 | card->leased = 1; | ||
1153 | while (card->ptype == ISDN_PTYPE_UNKNOWN) | ||
1154 | schedule_timeout_interruptible(10); | ||
1155 | schedule_timeout_interruptible(10); | ||
1156 | sprintf(cbuf, "00;FV2ON\n01;EAZ1\n02;EAZ2\n"); | ||
1157 | i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); | ||
1158 | printk(KERN_INFO | ||
1159 | "isdnloop: (%s) Leased-line mode enabled\n", | ||
1160 | CID); | ||
1161 | cmd.command = ISDN_STAT_RUN; | ||
1162 | cmd.driver = card->myid; | ||
1163 | cmd.arg = 0; | ||
1164 | card->interface.statcallb(&cmd); | ||
1165 | } | ||
1166 | } else { | ||
1167 | if (card->leased) { | ||
1168 | card->leased = 0; | ||
1169 | sprintf(cbuf, "00;FV2OFF\n"); | ||
1170 | i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); | ||
1171 | printk(KERN_INFO | ||
1172 | "isdnloop: (%s) Leased-line mode disabled\n", | ||
1173 | CID); | ||
1174 | cmd.command = ISDN_STAT_RUN; | ||
1175 | cmd.driver = card->myid; | ||
1176 | cmd.arg = 0; | ||
1177 | card->interface.statcallb(&cmd); | ||
1178 | } | ||
1179 | } | ||
1180 | return 0; | ||
1181 | default: | ||
1182 | return -EINVAL; | ||
1183 | } | ||
1184 | break; | ||
1185 | case ISDN_CMD_DIAL: | ||
1186 | if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) | ||
1187 | return -ENODEV; | ||
1188 | if (card->leased) | ||
1189 | break; | ||
1190 | if ((c->arg & 255) < ISDNLOOP_BCH) { | ||
1191 | char *p; | ||
1192 | char dcode[4]; | ||
1193 | |||
1194 | a = c->arg; | ||
1195 | p = c->parm.setup.phone; | ||
1196 | if (*p == 's' || *p == 'S') { | ||
1197 | /* Dial for SPV */ | ||
1198 | p++; | ||
1199 | strcpy(dcode, "SCA"); | ||
1200 | } else | ||
1201 | /* Normal Dial */ | ||
1202 | strcpy(dcode, "CAL"); | ||
1203 | snprintf(cbuf, sizeof(cbuf), | ||
1204 | "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1), | ||
1205 | dcode, p, c->parm.setup.si1, | ||
1206 | c->parm.setup.si2, c->parm.setup.eazmsn); | ||
1207 | i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); | ||
1208 | } | ||
1209 | break; | ||
1210 | case ISDN_CMD_ACCEPTD: | ||
1211 | if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) | ||
1212 | return -ENODEV; | ||
1213 | if (c->arg < ISDNLOOP_BCH) { | ||
1214 | a = c->arg + 1; | ||
1215 | cbuf[0] = 0; | ||
1216 | switch (card->l2_proto[a - 1]) { | ||
1217 | case ISDN_PROTO_L2_X75I: | ||
1218 | sprintf(cbuf, "%02d;BX75\n", (int) a); | ||
1219 | break; | ||
1220 | #ifdef CONFIG_ISDN_X25 | ||
1221 | case ISDN_PROTO_L2_X25DTE: | ||
1222 | sprintf(cbuf, "%02d;BX2T\n", (int) a); | ||
1223 | break; | ||
1224 | case ISDN_PROTO_L2_X25DCE: | ||
1225 | sprintf(cbuf, "%02d;BX2C\n", (int) a); | ||
1226 | break; | ||
1227 | #endif | ||
1228 | case ISDN_PROTO_L2_HDLC: | ||
1229 | sprintf(cbuf, "%02d;BTRA\n", (int) a); | ||
1230 | break; | ||
1231 | } | ||
1232 | if (strlen(cbuf)) | ||
1233 | i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); | ||
1234 | sprintf(cbuf, "%02d;DCON_R\n", (int) a); | ||
1235 | i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); | ||
1236 | } | ||
1237 | break; | ||
1238 | case ISDN_CMD_ACCEPTB: | ||
1239 | if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) | ||
1240 | return -ENODEV; | ||
1241 | if (c->arg < ISDNLOOP_BCH) { | ||
1242 | a = c->arg + 1; | ||
1243 | switch (card->l2_proto[a - 1]) { | ||
1244 | case ISDN_PROTO_L2_X75I: | ||
1245 | sprintf(cbuf, "%02d;BCON_R,BX75\n", (int) a); | ||
1246 | break; | ||
1247 | #ifdef CONFIG_ISDN_X25 | ||
1248 | case ISDN_PROTO_L2_X25DTE: | ||
1249 | sprintf(cbuf, "%02d;BCON_R,BX2T\n", (int) a); | ||
1250 | break; | ||
1251 | case ISDN_PROTO_L2_X25DCE: | ||
1252 | sprintf(cbuf, "%02d;BCON_R,BX2C\n", (int) a); | ||
1253 | break; | ||
1254 | #endif | ||
1255 | case ISDN_PROTO_L2_HDLC: | ||
1256 | sprintf(cbuf, "%02d;BCON_R,BTRA\n", (int) a); | ||
1257 | break; | ||
1258 | default: | ||
1259 | sprintf(cbuf, "%02d;BCON_R\n", (int) a); | ||
1260 | } | ||
1261 | printk(KERN_DEBUG "isdnloop writecmd '%s'\n", cbuf); | ||
1262 | i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); | ||
1263 | break; | ||
1264 | case ISDN_CMD_HANGUP: | ||
1265 | if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) | ||
1266 | return -ENODEV; | ||
1267 | if (c->arg < ISDNLOOP_BCH) { | ||
1268 | a = c->arg + 1; | ||
1269 | sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a); | ||
1270 | i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); | ||
1271 | } | ||
1272 | break; | ||
1273 | case ISDN_CMD_SETEAZ: | ||
1274 | if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) | ||
1275 | return -ENODEV; | ||
1276 | if (card->leased) | ||
1277 | break; | ||
1278 | if (c->arg < ISDNLOOP_BCH) { | ||
1279 | a = c->arg + 1; | ||
1280 | if (card->ptype == ISDN_PTYPE_EURO) { | ||
1281 | sprintf(cbuf, "%02d;MS%s%s\n", (int) a, | ||
1282 | c->parm.num[0] ? "N" : "ALL", c->parm.num); | ||
1283 | } else | ||
1284 | sprintf(cbuf, "%02d;EAZ%s\n", (int) a, | ||
1285 | c->parm.num[0] ? c->parm.num : (u_char *) "0123456789"); | ||
1286 | i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); | ||
1287 | } | ||
1288 | break; | ||
1289 | case ISDN_CMD_CLREAZ: | ||
1290 | if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) | ||
1291 | return -ENODEV; | ||
1292 | if (card->leased) | ||
1293 | break; | ||
1294 | if (c->arg < ISDNLOOP_BCH) { | ||
1295 | a = c->arg + 1; | ||
1296 | if (card->ptype == ISDN_PTYPE_EURO) | ||
1297 | sprintf(cbuf, "%02d;MSNC\n", (int) a); | ||
1298 | else | ||
1299 | sprintf(cbuf, "%02d;EAZC\n", (int) a); | ||
1300 | i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); | ||
1301 | } | ||
1302 | break; | ||
1303 | case ISDN_CMD_SETL2: | ||
1304 | if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) | ||
1305 | return -ENODEV; | ||
1306 | if ((c->arg & 255) < ISDNLOOP_BCH) { | ||
1307 | a = c->arg; | ||
1308 | switch (a >> 8) { | ||
1309 | case ISDN_PROTO_L2_X75I: | ||
1310 | sprintf(cbuf, "%02d;BX75\n", (int) (a & 255) + 1); | ||
1311 | break; | ||
1312 | #ifdef CONFIG_ISDN_X25 | ||
1313 | case ISDN_PROTO_L2_X25DTE: | ||
1314 | sprintf(cbuf, "%02d;BX2T\n", (int) (a & 255) + 1); | ||
1315 | break; | ||
1316 | case ISDN_PROTO_L2_X25DCE: | ||
1317 | sprintf(cbuf, "%02d;BX2C\n", (int) (a & 255) + 1); | ||
1318 | break; | ||
1319 | #endif | ||
1320 | case ISDN_PROTO_L2_HDLC: | ||
1321 | sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1); | ||
1322 | break; | ||
1323 | case ISDN_PROTO_L2_TRANS: | ||
1324 | sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1); | ||
1325 | break; | ||
1326 | default: | ||
1327 | return -EINVAL; | ||
1328 | } | ||
1329 | i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); | ||
1330 | card->l2_proto[a & 255] = (a >> 8); | ||
1331 | } | ||
1332 | break; | ||
1333 | case ISDN_CMD_SETL3: | ||
1334 | if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) | ||
1335 | return -ENODEV; | ||
1336 | return 0; | ||
1337 | default: | ||
1338 | return -EINVAL; | ||
1339 | } | ||
1340 | } | ||
1341 | return 0; | ||
1342 | } | ||
1343 | |||
1344 | /* | ||
1345 | * Find card with given driverId | ||
1346 | */ | ||
1347 | static inline isdnloop_card * | ||
1348 | isdnloop_findcard(int driverid) | ||
1349 | { | ||
1350 | isdnloop_card *p = cards; | ||
1351 | |||
1352 | while (p) { | ||
1353 | if (p->myid == driverid) | ||
1354 | return p; | ||
1355 | p = p->next; | ||
1356 | } | ||
1357 | return (isdnloop_card *) 0; | ||
1358 | } | ||
1359 | |||
1360 | /* | ||
1361 | * Wrapper functions for interface to linklevel | ||
1362 | */ | ||
1363 | static int | ||
1364 | if_command(isdn_ctrl *c) | ||
1365 | { | ||
1366 | isdnloop_card *card = isdnloop_findcard(c->driver); | ||
1367 | |||
1368 | if (card) | ||
1369 | return isdnloop_command(c, card); | ||
1370 | printk(KERN_ERR | ||
1371 | "isdnloop: if_command called with invalid driverId!\n"); | ||
1372 | return -ENODEV; | ||
1373 | } | ||
1374 | |||
1375 | static int | ||
1376 | if_writecmd(const u_char __user *buf, int len, int id, int channel) | ||
1377 | { | ||
1378 | isdnloop_card *card = isdnloop_findcard(id); | ||
1379 | |||
1380 | if (card) { | ||
1381 | if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) | ||
1382 | return -ENODEV; | ||
1383 | return isdnloop_writecmd(buf, len, 1, card); | ||
1384 | } | ||
1385 | printk(KERN_ERR | ||
1386 | "isdnloop: if_writecmd called with invalid driverId!\n"); | ||
1387 | return -ENODEV; | ||
1388 | } | ||
1389 | |||
1390 | static int | ||
1391 | if_readstatus(u_char __user *buf, int len, int id, int channel) | ||
1392 | { | ||
1393 | isdnloop_card *card = isdnloop_findcard(id); | ||
1394 | |||
1395 | if (card) { | ||
1396 | if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) | ||
1397 | return -ENODEV; | ||
1398 | return isdnloop_readstatus(buf, len, card); | ||
1399 | } | ||
1400 | printk(KERN_ERR | ||
1401 | "isdnloop: if_readstatus called with invalid driverId!\n"); | ||
1402 | return -ENODEV; | ||
1403 | } | ||
1404 | |||
1405 | static int | ||
1406 | if_sendbuf(int id, int channel, int ack, struct sk_buff *skb) | ||
1407 | { | ||
1408 | isdnloop_card *card = isdnloop_findcard(id); | ||
1409 | |||
1410 | if (card) { | ||
1411 | if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) | ||
1412 | return -ENODEV; | ||
1413 | /* ack request stored in skb scratch area */ | ||
1414 | *(skb->head) = ack; | ||
1415 | return isdnloop_sendbuf(channel, skb, card); | ||
1416 | } | ||
1417 | printk(KERN_ERR | ||
1418 | "isdnloop: if_sendbuf called with invalid driverId!\n"); | ||
1419 | return -ENODEV; | ||
1420 | } | ||
1421 | |||
1422 | /* | ||
1423 | * Allocate a new card-struct, initialize it | ||
1424 | * link it into cards-list and register it at linklevel. | ||
1425 | */ | ||
1426 | static isdnloop_card * | ||
1427 | isdnloop_initcard(char *id) | ||
1428 | { | ||
1429 | isdnloop_card *card; | ||
1430 | int i; | ||
1431 | card = kzalloc(sizeof(isdnloop_card), GFP_KERNEL); | ||
1432 | if (!card) { | ||
1433 | printk(KERN_WARNING | ||
1434 | "isdnloop: (%s) Could not allocate card-struct.\n", id); | ||
1435 | return (isdnloop_card *) 0; | ||
1436 | } | ||
1437 | card->interface.owner = THIS_MODULE; | ||
1438 | card->interface.channels = ISDNLOOP_BCH; | ||
1439 | card->interface.hl_hdrlen = 1; /* scratch area for storing ack flag*/ | ||
1440 | card->interface.maxbufsize = 4000; | ||
1441 | card->interface.command = if_command; | ||
1442 | card->interface.writebuf_skb = if_sendbuf; | ||
1443 | card->interface.writecmd = if_writecmd; | ||
1444 | card->interface.readstat = if_readstatus; | ||
1445 | card->interface.features = ISDN_FEATURE_L2_X75I | | ||
1446 | #ifdef CONFIG_ISDN_X25 | ||
1447 | ISDN_FEATURE_L2_X25DTE | | ||
1448 | ISDN_FEATURE_L2_X25DCE | | ||
1449 | #endif | ||
1450 | ISDN_FEATURE_L2_HDLC | | ||
1451 | ISDN_FEATURE_L3_TRANS | | ||
1452 | ISDN_FEATURE_P_UNKNOWN; | ||
1453 | card->ptype = ISDN_PTYPE_UNKNOWN; | ||
1454 | strlcpy(card->interface.id, id, sizeof(card->interface.id)); | ||
1455 | card->msg_buf_write = card->msg_buf; | ||
1456 | card->msg_buf_read = card->msg_buf; | ||
1457 | card->msg_buf_end = &card->msg_buf[sizeof(card->msg_buf) - 1]; | ||
1458 | for (i = 0; i < ISDNLOOP_BCH; i++) { | ||
1459 | card->l2_proto[i] = ISDN_PROTO_L2_X75I; | ||
1460 | skb_queue_head_init(&card->bqueue[i]); | ||
1461 | } | ||
1462 | skb_queue_head_init(&card->dqueue); | ||
1463 | spin_lock_init(&card->isdnloop_lock); | ||
1464 | card->next = cards; | ||
1465 | cards = card; | ||
1466 | if (!register_isdn(&card->interface)) { | ||
1467 | cards = cards->next; | ||
1468 | printk(KERN_WARNING | ||
1469 | "isdnloop: Unable to register %s\n", id); | ||
1470 | kfree(card); | ||
1471 | return (isdnloop_card *) 0; | ||
1472 | } | ||
1473 | card->myid = card->interface.channels; | ||
1474 | return card; | ||
1475 | } | ||
1476 | |||
1477 | static int | ||
1478 | isdnloop_addcard(char *id1) | ||
1479 | { | ||
1480 | isdnloop_card *card; | ||
1481 | card = isdnloop_initcard(id1); | ||
1482 | if (!card) { | ||
1483 | return -EIO; | ||
1484 | } | ||
1485 | printk(KERN_INFO | ||
1486 | "isdnloop: (%s) virtual card added\n", | ||
1487 | card->interface.id); | ||
1488 | return 0; | ||
1489 | } | ||
1490 | |||
1491 | static int __init | ||
1492 | isdnloop_init(void) | ||
1493 | { | ||
1494 | if (isdnloop_id) | ||
1495 | return isdnloop_addcard(isdnloop_id); | ||
1496 | |||
1497 | return 0; | ||
1498 | } | ||
1499 | |||
1500 | static void __exit | ||
1501 | isdnloop_exit(void) | ||
1502 | { | ||
1503 | isdn_ctrl cmd; | ||
1504 | isdnloop_card *card = cards; | ||
1505 | isdnloop_card *last; | ||
1506 | int i; | ||
1507 | |||
1508 | isdnloop_stopallcards(); | ||
1509 | while (card) { | ||
1510 | cmd.command = ISDN_STAT_UNLOAD; | ||
1511 | cmd.driver = card->myid; | ||
1512 | card->interface.statcallb(&cmd); | ||
1513 | for (i = 0; i < ISDNLOOP_BCH; i++) | ||
1514 | isdnloop_free_queue(card, i); | ||
1515 | card = card->next; | ||
1516 | } | ||
1517 | card = cards; | ||
1518 | while (card) { | ||
1519 | last = card; | ||
1520 | skb_queue_purge(&card->dqueue); | ||
1521 | card = card->next; | ||
1522 | kfree(last); | ||
1523 | } | ||
1524 | printk(KERN_NOTICE "isdnloop-ISDN-driver unloaded\n"); | ||
1525 | } | ||
1526 | |||
1527 | module_init(isdnloop_init); | ||
1528 | module_exit(isdnloop_exit); | ||
diff --git a/drivers/isdn/isdnloop/isdnloop.h b/drivers/isdn/isdnloop/isdnloop.h deleted file mode 100644 index e9e035552bb4..000000000000 --- a/drivers/isdn/isdnloop/isdnloop.h +++ /dev/null | |||
@@ -1,112 +0,0 @@ | |||
1 | /* $Id: isdnloop.h,v 1.5.6.3 2001/09/23 22:24:56 kai Exp $ | ||
2 | * | ||
3 | * Loopback lowlevel module for testing of linklevel. | ||
4 | * | ||
5 | * Copyright 1997 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 | #ifndef isdnloop_h | ||
13 | #define isdnloop_h | ||
14 | |||
15 | #define ISDNLOOP_IOCTL_DEBUGVAR 0 | ||
16 | #define ISDNLOOP_IOCTL_ADDCARD 1 | ||
17 | #define ISDNLOOP_IOCTL_LEASEDCFG 2 | ||
18 | #define ISDNLOOP_IOCTL_STARTUP 3 | ||
19 | |||
20 | /* Struct for adding new cards */ | ||
21 | typedef struct isdnloop_cdef { | ||
22 | char id1[10]; | ||
23 | } isdnloop_cdef; | ||
24 | |||
25 | /* Struct for configuring cards */ | ||
26 | typedef struct isdnloop_sdef { | ||
27 | int ptype; | ||
28 | char num[3][20]; | ||
29 | } isdnloop_sdef; | ||
30 | |||
31 | #if defined(__KERNEL__) || defined(__DEBUGVAR__) | ||
32 | |||
33 | #ifdef __KERNEL__ | ||
34 | /* Kernel includes */ | ||
35 | |||
36 | #include <linux/errno.h> | ||
37 | #include <linux/fs.h> | ||
38 | #include <linux/major.h> | ||
39 | #include <asm/io.h> | ||
40 | #include <linux/kernel.h> | ||
41 | #include <linux/signal.h> | ||
42 | #include <linux/slab.h> | ||
43 | #include <linux/mm.h> | ||
44 | #include <linux/mman.h> | ||
45 | #include <linux/ioport.h> | ||
46 | #include <linux/timer.h> | ||
47 | #include <linux/wait.h> | ||
48 | #include <linux/isdnif.h> | ||
49 | |||
50 | #endif /* __KERNEL__ */ | ||
51 | |||
52 | #define ISDNLOOP_FLAGS_B1ACTIVE 1 /* B-Channel-1 is open */ | ||
53 | #define ISDNLOOP_FLAGS_B2ACTIVE 2 /* B-Channel-2 is open */ | ||
54 | #define ISDNLOOP_FLAGS_RUNNING 4 /* Cards driver activated */ | ||
55 | #define ISDNLOOP_FLAGS_RBTIMER 8 /* scheduling of B-Channel-poll */ | ||
56 | #define ISDNLOOP_TIMER_BCREAD 1 /* B-Channel poll-cycle */ | ||
57 | #define ISDNLOOP_TIMER_DCREAD (HZ/2) /* D-Channel poll-cycle */ | ||
58 | #define ISDNLOOP_TIMER_ALERTWAIT (10 * HZ) /* Alert timeout */ | ||
59 | #define ISDNLOOP_MAX_SQUEUE 65536 /* Max. outstanding send-data */ | ||
60 | #define ISDNLOOP_BCH 2 /* channels per card */ | ||
61 | |||
62 | /* | ||
63 | * Per card driver data | ||
64 | */ | ||
65 | typedef struct isdnloop_card { | ||
66 | struct isdnloop_card *next; /* Pointer to next device struct */ | ||
67 | struct isdnloop_card | ||
68 | *rcard[ISDNLOOP_BCH]; /* Pointer to 'remote' card */ | ||
69 | int rch[ISDNLOOP_BCH]; /* 'remote' channel */ | ||
70 | int myid; /* Driver-Nr. assigned by linklevel */ | ||
71 | int leased; /* Flag: This Adapter is connected */ | ||
72 | /* to a leased line */ | ||
73 | int sil[ISDNLOOP_BCH]; /* SI's to listen for */ | ||
74 | char eazlist[ISDNLOOP_BCH][11]; | ||
75 | /* EAZ's to listen for */ | ||
76 | char s0num[3][20]; /* 1TR6 base-number or MSN's */ | ||
77 | unsigned short flags; /* Statusflags */ | ||
78 | int ptype; /* Protocol type (1TR6 or Euro) */ | ||
79 | struct timer_list st_timer; /* Timer for Status-Polls */ | ||
80 | struct timer_list rb_timer; /* Timer for B-Channel-Polls */ | ||
81 | struct timer_list | ||
82 | c_timer[ISDNLOOP_BCH]; /* Timer for Alerting */ | ||
83 | int l2_proto[ISDNLOOP_BCH]; /* Current layer-2-protocol */ | ||
84 | isdn_if interface; /* Interface to upper layer */ | ||
85 | int iptr; /* Index to imsg-buffer */ | ||
86 | char imsg[60]; /* Internal buf for status-parsing */ | ||
87 | int optr; /* Index to omsg-buffer */ | ||
88 | char omsg[60]; /* Internal buf for cmd-parsing */ | ||
89 | char msg_buf[2048]; /* Buffer for status-messages */ | ||
90 | char *msg_buf_write; /* Writepointer for statusbuffer */ | ||
91 | char *msg_buf_read; /* Readpointer for statusbuffer */ | ||
92 | char *msg_buf_end; /* Pointer to end of statusbuffer */ | ||
93 | int sndcount[ISDNLOOP_BCH]; /* Byte-counters for B-Ch.-send */ | ||
94 | struct sk_buff_head | ||
95 | bqueue[ISDNLOOP_BCH]; /* B-Channel queues */ | ||
96 | struct sk_buff_head dqueue; /* D-Channel queue */ | ||
97 | spinlock_t isdnloop_lock; | ||
98 | } isdnloop_card; | ||
99 | |||
100 | /* | ||
101 | * Main driver data | ||
102 | */ | ||
103 | #ifdef __KERNEL__ | ||
104 | static isdnloop_card *cards = (isdnloop_card *) 0; | ||
105 | #endif /* __KERNEL__ */ | ||
106 | |||
107 | /* Utility-Macros */ | ||
108 | |||
109 | #define CID (card->interface.id) | ||
110 | |||
111 | #endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */ | ||
112 | #endif /* isdnloop_h */ | ||
diff --git a/include/linux/concap.h b/include/linux/concap.h deleted file mode 100644 index 977acb3d1fb2..000000000000 --- a/include/linux/concap.h +++ /dev/null | |||
@@ -1,112 +0,0 @@ | |||
1 | /* $Id: concap.h,v 1.3.2.2 2004/01/12 23:08:35 keil Exp $ | ||
2 | * | ||
3 | * Copyright 1997 by Henner Eisen <eis@baty.hanse.de> | ||
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 | #ifndef _LINUX_CONCAP_H | ||
10 | #define _LINUX_CONCAP_H | ||
11 | |||
12 | #include <linux/skbuff.h> | ||
13 | #include <linux/netdevice.h> | ||
14 | |||
15 | /* Stuff to support encapsulation protocols genericly. The encapsulation | ||
16 | protocol is processed at the uppermost layer of the network interface. | ||
17 | |||
18 | Based on a ideas developed in a 'synchronous device' thread in the | ||
19 | linux-x25 mailing list contributed by Alan Cox, Thomasz Motylewski | ||
20 | and Jonathan Naylor. | ||
21 | |||
22 | For more documetation on this refer to Documentation/isdn/README.concap | ||
23 | */ | ||
24 | |||
25 | struct concap_proto_ops; | ||
26 | struct concap_device_ops; | ||
27 | |||
28 | /* this manages all data needed by the encapsulation protocol | ||
29 | */ | ||
30 | struct concap_proto{ | ||
31 | struct net_device *net_dev; /* net device using our service */ | ||
32 | struct concap_device_ops *dops; /* callbacks provided by device */ | ||
33 | struct concap_proto_ops *pops; /* callbacks provided by us */ | ||
34 | spinlock_t lock; | ||
35 | int flags; | ||
36 | void *proto_data; /* protocol specific private data, to | ||
37 | be accessed via *pops methods only*/ | ||
38 | /* | ||
39 | : | ||
40 | whatever | ||
41 | : | ||
42 | */ | ||
43 | }; | ||
44 | |||
45 | /* Operations to be supported by the net device. Called by the encapsulation | ||
46 | * protocol entity. No receive method is offered because the encapsulation | ||
47 | * protocol directly calls netif_rx(). | ||
48 | */ | ||
49 | struct concap_device_ops{ | ||
50 | |||
51 | /* to request data is submitted by device*/ | ||
52 | int (*data_req)(struct concap_proto *, struct sk_buff *); | ||
53 | |||
54 | /* Control methods must be set to NULL by devices which do not | ||
55 | support connection control.*/ | ||
56 | /* to request a connection is set up */ | ||
57 | int (*connect_req)(struct concap_proto *); | ||
58 | |||
59 | /* to request a connection is released */ | ||
60 | int (*disconn_req)(struct concap_proto *); | ||
61 | }; | ||
62 | |||
63 | /* Operations to be supported by the encapsulation protocol. Called by | ||
64 | * device driver. | ||
65 | */ | ||
66 | struct concap_proto_ops{ | ||
67 | |||
68 | /* create a new encapsulation protocol instance of same type */ | ||
69 | struct concap_proto * (*proto_new) (void); | ||
70 | |||
71 | /* delete encapsulation protocol instance and free all its resources. | ||
72 | cprot may no loger be referenced after calling this */ | ||
73 | void (*proto_del)(struct concap_proto *cprot); | ||
74 | |||
75 | /* initialize the protocol's data. To be called at interface startup | ||
76 | or when the device driver resets the interface. All services of the | ||
77 | encapsulation protocol may be used after this*/ | ||
78 | int (*restart)(struct concap_proto *cprot, | ||
79 | struct net_device *ndev, | ||
80 | struct concap_device_ops *dops); | ||
81 | |||
82 | /* inactivate an encapsulation protocol instance. The encapsulation | ||
83 | protocol may not call any *dops methods after this. */ | ||
84 | int (*close)(struct concap_proto *cprot); | ||
85 | |||
86 | /* process a frame handed down to us by upper layer */ | ||
87 | int (*encap_and_xmit)(struct concap_proto *cprot, struct sk_buff *skb); | ||
88 | |||
89 | /* to be called for each data entity received from lower layer*/ | ||
90 | int (*data_ind)(struct concap_proto *cprot, struct sk_buff *skb); | ||
91 | |||
92 | /* to be called when a connection was set up/down. | ||
93 | Protocols that don't process these primitives might fill in | ||
94 | dummy methods here */ | ||
95 | int (*connect_ind)(struct concap_proto *cprot); | ||
96 | int (*disconn_ind)(struct concap_proto *cprot); | ||
97 | /* | ||
98 | Some network device support functions, like net_header(), rebuild_header(), | ||
99 | and others, that depend solely on the encapsulation protocol, might | ||
100 | be provided here, too. The net device would just fill them in its | ||
101 | corresponding fields when it is opened. | ||
102 | */ | ||
103 | }; | ||
104 | |||
105 | /* dummy restart/close/connect/reset/disconn methods | ||
106 | */ | ||
107 | extern int concap_nop(struct concap_proto *cprot); | ||
108 | |||
109 | /* dummy submit method | ||
110 | */ | ||
111 | extern int concap_drop_skb(struct concap_proto *cprot, struct sk_buff *skb); | ||
112 | #endif | ||
diff --git a/include/linux/isdn.h b/include/linux/isdn.h deleted file mode 100644 index df97c8444f5d..000000000000 --- a/include/linux/isdn.h +++ /dev/null | |||
@@ -1,473 +0,0 @@ | |||
1 | /* $Id: isdn.h,v 1.125.2.3 2004/02/10 01:07:14 keil Exp $ | ||
2 | * | ||
3 | * Main header for the Linux ISDN subsystem (linklevel). | ||
4 | * | ||
5 | * Copyright 1994,95,96 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 | #ifndef __ISDN_H__ | ||
14 | #define __ISDN_H__ | ||
15 | |||
16 | |||
17 | #include <linux/errno.h> | ||
18 | #include <linux/fs.h> | ||
19 | #include <linux/major.h> | ||
20 | #include <asm/io.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/signal.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/timer.h> | ||
25 | #include <linux/wait.h> | ||
26 | #include <linux/tty.h> | ||
27 | #include <linux/tty_flip.h> | ||
28 | #include <linux/serial_reg.h> | ||
29 | #include <linux/fcntl.h> | ||
30 | #include <linux/types.h> | ||
31 | #include <linux/interrupt.h> | ||
32 | #include <linux/ip.h> | ||
33 | #include <linux/in.h> | ||
34 | #include <linux/netdevice.h> | ||
35 | #include <linux/etherdevice.h> | ||
36 | #include <linux/skbuff.h> | ||
37 | #include <linux/tcp.h> | ||
38 | #include <linux/mutex.h> | ||
39 | #include <uapi/linux/isdn.h> | ||
40 | |||
41 | #define ISDN_TTY_MAJOR 43 | ||
42 | #define ISDN_TTYAUX_MAJOR 44 | ||
43 | #define ISDN_MAJOR 45 | ||
44 | |||
45 | /* The minor-devicenumbers for Channel 0 and 1 are used as arguments for | ||
46 | * physical Channel-Mapping, so they MUST NOT be changed without changing | ||
47 | * the correspondent code in isdn.c | ||
48 | */ | ||
49 | |||
50 | #define ISDN_MINOR_B 0 | ||
51 | #define ISDN_MINOR_BMAX (ISDN_MAX_CHANNELS-1) | ||
52 | #define ISDN_MINOR_CTRL 64 | ||
53 | #define ISDN_MINOR_CTRLMAX (64 + (ISDN_MAX_CHANNELS-1)) | ||
54 | #define ISDN_MINOR_PPP 128 | ||
55 | #define ISDN_MINOR_PPPMAX (128 + (ISDN_MAX_CHANNELS-1)) | ||
56 | #define ISDN_MINOR_STATUS 255 | ||
57 | |||
58 | #ifdef CONFIG_ISDN_PPP | ||
59 | |||
60 | #ifdef CONFIG_ISDN_PPP_VJ | ||
61 | # include <net/slhc_vj.h> | ||
62 | #endif | ||
63 | |||
64 | #include <linux/ppp_defs.h> | ||
65 | #include <linux/ppp-ioctl.h> | ||
66 | |||
67 | #include <linux/isdn_ppp.h> | ||
68 | #endif | ||
69 | |||
70 | #ifdef CONFIG_ISDN_X25 | ||
71 | # include <linux/concap.h> | ||
72 | #endif | ||
73 | |||
74 | #include <linux/isdnif.h> | ||
75 | |||
76 | #define ISDN_DRVIOCTL_MASK 0x7f /* Mask for Device-ioctl */ | ||
77 | |||
78 | /* Until now unused */ | ||
79 | #define ISDN_SERVICE_VOICE 1 | ||
80 | #define ISDN_SERVICE_AB 1<<1 | ||
81 | #define ISDN_SERVICE_X21 1<<2 | ||
82 | #define ISDN_SERVICE_G4 1<<3 | ||
83 | #define ISDN_SERVICE_BTX 1<<4 | ||
84 | #define ISDN_SERVICE_DFUE 1<<5 | ||
85 | #define ISDN_SERVICE_X25 1<<6 | ||
86 | #define ISDN_SERVICE_TTX 1<<7 | ||
87 | #define ISDN_SERVICE_MIXED 1<<8 | ||
88 | #define ISDN_SERVICE_FW 1<<9 | ||
89 | #define ISDN_SERVICE_GTEL 1<<10 | ||
90 | #define ISDN_SERVICE_BTXN 1<<11 | ||
91 | #define ISDN_SERVICE_BTEL 1<<12 | ||
92 | |||
93 | /* Macros checking plain usage */ | ||
94 | #define USG_NONE(x) ((x & ISDN_USAGE_MASK)==ISDN_USAGE_NONE) | ||
95 | #define USG_RAW(x) ((x & ISDN_USAGE_MASK)==ISDN_USAGE_RAW) | ||
96 | #define USG_MODEM(x) ((x & ISDN_USAGE_MASK)==ISDN_USAGE_MODEM) | ||
97 | #define USG_VOICE(x) ((x & ISDN_USAGE_MASK)==ISDN_USAGE_VOICE) | ||
98 | #define USG_NET(x) ((x & ISDN_USAGE_MASK)==ISDN_USAGE_NET) | ||
99 | #define USG_FAX(x) ((x & ISDN_USAGE_MASK)==ISDN_USAGE_FAX) | ||
100 | #define USG_OUTGOING(x) ((x & ISDN_USAGE_OUTGOING)==ISDN_USAGE_OUTGOING) | ||
101 | #define USG_MODEMORVOICE(x) (((x & ISDN_USAGE_MASK)==ISDN_USAGE_MODEM) || \ | ||
102 | ((x & ISDN_USAGE_MASK)==ISDN_USAGE_VOICE) ) | ||
103 | |||
104 | /* Timer-delays and scheduling-flags */ | ||
105 | #define ISDN_TIMER_RES 4 /* Main Timer-Resolution */ | ||
106 | #define ISDN_TIMER_02SEC (HZ/ISDN_TIMER_RES/5) /* Slow-Timer1 .2 sec */ | ||
107 | #define ISDN_TIMER_1SEC (HZ/ISDN_TIMER_RES) /* Slow-Timer2 1 sec */ | ||
108 | #define ISDN_TIMER_RINGING 5 /* tty RINGs = ISDN_TIMER_1SEC * this factor */ | ||
109 | #define ISDN_TIMER_KEEPINT 10 /* Cisco-Keepalive = ISDN_TIMER_1SEC * this factor */ | ||
110 | #define ISDN_TIMER_MODEMREAD 1 | ||
111 | #define ISDN_TIMER_MODEMPLUS 2 | ||
112 | #define ISDN_TIMER_MODEMRING 4 | ||
113 | #define ISDN_TIMER_MODEMXMIT 8 | ||
114 | #define ISDN_TIMER_NETDIAL 16 | ||
115 | #define ISDN_TIMER_NETHANGUP 32 | ||
116 | #define ISDN_TIMER_CARRIER 256 /* Wait for Carrier */ | ||
117 | #define ISDN_TIMER_FAST (ISDN_TIMER_MODEMREAD | ISDN_TIMER_MODEMPLUS | \ | ||
118 | ISDN_TIMER_MODEMXMIT) | ||
119 | #define ISDN_TIMER_SLOW (ISDN_TIMER_MODEMRING | ISDN_TIMER_NETHANGUP | \ | ||
120 | ISDN_TIMER_NETDIAL | ISDN_TIMER_CARRIER) | ||
121 | |||
122 | /* Timeout-Values for isdn_net_dial() */ | ||
123 | #define ISDN_TIMER_DTIMEOUT10 (10*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1))) | ||
124 | #define ISDN_TIMER_DTIMEOUT15 (15*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1))) | ||
125 | #define ISDN_TIMER_DTIMEOUT60 (60*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1))) | ||
126 | |||
127 | /* GLOBAL_FLAGS */ | ||
128 | #define ISDN_GLOBAL_STOPPED 1 | ||
129 | |||
130 | /*=================== Start of ip-over-ISDN stuff =========================*/ | ||
131 | |||
132 | /* Feature- and status-flags for a net-interface */ | ||
133 | #define ISDN_NET_CONNECTED 0x01 /* Bound to ISDN-Channel */ | ||
134 | #define ISDN_NET_SECURE 0x02 /* Accept calls from phonelist only */ | ||
135 | #define ISDN_NET_CALLBACK 0x04 /* activate callback */ | ||
136 | #define ISDN_NET_CBHUP 0x08 /* hangup before callback */ | ||
137 | #define ISDN_NET_CBOUT 0x10 /* remote machine does callback */ | ||
138 | |||
139 | #define ISDN_NET_MAGIC 0x49344C02 /* for paranoia-checking */ | ||
140 | |||
141 | /* Phone-list-element */ | ||
142 | typedef struct { | ||
143 | void *next; | ||
144 | char num[ISDN_MSNLEN]; | ||
145 | } isdn_net_phone; | ||
146 | |||
147 | /* | ||
148 | Principles when extending structures for generic encapsulation protocol | ||
149 | ("concap") support: | ||
150 | - Stuff which is hardware specific (here i4l-specific) goes in | ||
151 | the netdev -> local structure (here: isdn_net_local) | ||
152 | - Stuff which is encapsulation protocol specific goes in the structure | ||
153 | which holds the linux device structure (here: isdn_net_device) | ||
154 | */ | ||
155 | |||
156 | /* Local interface-data */ | ||
157 | typedef struct isdn_net_local_s { | ||
158 | ulong magic; | ||
159 | struct net_device_stats stats; /* Ethernet Statistics */ | ||
160 | int isdn_device; /* Index to isdn-device */ | ||
161 | int isdn_channel; /* Index to isdn-channel */ | ||
162 | int ppp_slot; /* PPPD device slot number */ | ||
163 | int pre_device; /* Preselected isdn-device */ | ||
164 | int pre_channel; /* Preselected isdn-channel */ | ||
165 | int exclusive; /* If non-zero idx to reserved chan.*/ | ||
166 | int flags; /* Connection-flags */ | ||
167 | int dialretry; /* Counter for Dialout-retries */ | ||
168 | int dialmax; /* Max. Number of Dial-retries */ | ||
169 | int cbdelay; /* Delay before Callback starts */ | ||
170 | int dtimer; /* Timeout-counter for dialing */ | ||
171 | char msn[ISDN_MSNLEN]; /* MSNs/EAZs for this interface */ | ||
172 | u_char cbhup; /* Flag: Reject Call before Callback*/ | ||
173 | u_char dialstate; /* State for dialing */ | ||
174 | u_char p_encap; /* Packet encapsulation */ | ||
175 | /* 0 = Ethernet over ISDN */ | ||
176 | /* 1 = RAW-IP */ | ||
177 | /* 2 = IP with type field */ | ||
178 | u_char l2_proto; /* Layer-2-protocol */ | ||
179 | /* See ISDN_PROTO_L2..-constants in */ | ||
180 | /* isdnif.h */ | ||
181 | /* 0 = X75/LAPB with I-Frames */ | ||
182 | /* 1 = X75/LAPB with UI-Frames */ | ||
183 | /* 2 = X75/LAPB with BUI-Frames */ | ||
184 | /* 3 = HDLC */ | ||
185 | u_char l3_proto; /* Layer-3-protocol */ | ||
186 | /* See ISDN_PROTO_L3..-constants in */ | ||
187 | /* isdnif.h */ | ||
188 | /* 0 = Transparent */ | ||
189 | int huptimer; /* Timeout-counter for auto-hangup */ | ||
190 | int charge; /* Counter for charging units */ | ||
191 | ulong chargetime; /* Timer for Charging info */ | ||
192 | int hupflags; /* Flags for charge-unit-hangup: */ | ||
193 | /* bit0: chargeint is invalid */ | ||
194 | /* bit1: Getting charge-interval */ | ||
195 | /* bit2: Do charge-unit-hangup */ | ||
196 | /* bit3: Do hangup even on incoming */ | ||
197 | int outgoing; /* Flag: outgoing call */ | ||
198 | int onhtime; /* Time to keep link up */ | ||
199 | int chargeint; /* Interval between charge-infos */ | ||
200 | int onum; /* Flag: at least 1 outgoing number */ | ||
201 | int cps; /* current speed of this interface */ | ||
202 | int transcount; /* byte-counter for cps-calculation */ | ||
203 | int sqfull; /* Flag: netdev-queue overloaded */ | ||
204 | ulong sqfull_stamp; /* Start-Time of overload */ | ||
205 | ulong slavedelay; /* Dynamic bundling delaytime */ | ||
206 | int triggercps; /* BogoCPS needed for trigger slave */ | ||
207 | isdn_net_phone *phone[2]; /* List of remote-phonenumbers */ | ||
208 | /* phone[0] = Incoming Numbers */ | ||
209 | /* phone[1] = Outgoing Numbers */ | ||
210 | isdn_net_phone *dial; /* Pointer to dialed number */ | ||
211 | struct net_device *master; /* Ptr to Master device for slaves */ | ||
212 | struct net_device *slave; /* Ptr to Slave device for masters */ | ||
213 | struct isdn_net_local_s *next; /* Ptr to next link in bundle */ | ||
214 | struct isdn_net_local_s *last; /* Ptr to last link in bundle */ | ||
215 | struct isdn_net_dev_s *netdev; /* Ptr to netdev */ | ||
216 | struct sk_buff_head super_tx_queue; /* List of supervisory frames to */ | ||
217 | /* be transmitted asap */ | ||
218 | atomic_t frame_cnt; /* number of frames currently */ | ||
219 | /* queued in HL driver */ | ||
220 | /* Ptr to orig. hard_header_cache */ | ||
221 | spinlock_t xmit_lock; /* used to protect the xmit path of */ | ||
222 | /* a particular channel (including */ | ||
223 | /* the frame_cnt */ | ||
224 | |||
225 | int pppbind; /* ippp device for bindings */ | ||
226 | int dialtimeout; /* How long shall we try on dialing? (jiffies) */ | ||
227 | int dialwait; /* How long shall we wait after failed attempt? (jiffies) */ | ||
228 | ulong dialstarted; /* jiffies of first dialing-attempt */ | ||
229 | ulong dialwait_timer; /* jiffies of earliest next dialing-attempt */ | ||
230 | int huptimeout; /* How long will the connection be up? (seconds) */ | ||
231 | #ifdef CONFIG_ISDN_X25 | ||
232 | struct concap_device_ops *dops; /* callbacks used by encapsulator */ | ||
233 | #endif | ||
234 | /* use an own struct for that in later versions */ | ||
235 | ulong cisco_myseq; /* Local keepalive seq. for Cisco */ | ||
236 | ulong cisco_mineseen; /* returned keepalive seq. from remote */ | ||
237 | ulong cisco_yourseq; /* Remote keepalive seq. for Cisco */ | ||
238 | int cisco_keepalive_period; /* keepalive period */ | ||
239 | ulong cisco_last_slarp_in; /* jiffie of last keepalive packet we received */ | ||
240 | char cisco_line_state; /* state of line according to keepalive packets */ | ||
241 | char cisco_debserint; /* debugging flag of cisco hdlc with slarp */ | ||
242 | struct timer_list cisco_timer; | ||
243 | struct work_struct tqueue; | ||
244 | } isdn_net_local; | ||
245 | |||
246 | /* the interface itself */ | ||
247 | typedef struct isdn_net_dev_s { | ||
248 | isdn_net_local *local; | ||
249 | isdn_net_local *queue; /* circular list of all bundled | ||
250 | channels, which are currently | ||
251 | online */ | ||
252 | spinlock_t queue_lock; /* lock to protect queue */ | ||
253 | void *next; /* Pointer to next isdn-interface */ | ||
254 | struct net_device *dev; /* interface to upper levels */ | ||
255 | #ifdef CONFIG_ISDN_PPP | ||
256 | ippp_bundle * pb; /* pointer to the common bundle structure | ||
257 | * with the per-bundle data */ | ||
258 | #endif | ||
259 | #ifdef CONFIG_ISDN_X25 | ||
260 | struct concap_proto *cprot; /* connection oriented encapsulation protocol */ | ||
261 | #endif | ||
262 | |||
263 | } isdn_net_dev; | ||
264 | |||
265 | /*===================== End of ip-over-ISDN stuff ===========================*/ | ||
266 | |||
267 | /*======================= Start of ISDN-tty stuff ===========================*/ | ||
268 | |||
269 | #define ISDN_ASYNC_MAGIC 0x49344C01 /* for paranoia-checking */ | ||
270 | #define ISDN_SERIAL_XMIT_SIZE 1024 /* Default bufsize for write */ | ||
271 | #define ISDN_SERIAL_XMIT_MAX 4000 /* Maximum bufsize for write */ | ||
272 | |||
273 | #ifdef CONFIG_ISDN_AUDIO | ||
274 | /* For using sk_buffs with audio we need some private variables | ||
275 | * within each sk_buff. For this purpose, we declare a struct here, | ||
276 | * and put it always at the private skb->cb data array. A few macros help | ||
277 | * accessing the variables. | ||
278 | */ | ||
279 | typedef struct _isdn_audio_data { | ||
280 | unsigned short dle_count; | ||
281 | unsigned char lock; | ||
282 | } isdn_audio_data_t; | ||
283 | |||
284 | #define ISDN_AUDIO_SKB_DLECOUNT(skb) (((isdn_audio_data_t *)&skb->cb[0])->dle_count) | ||
285 | #define ISDN_AUDIO_SKB_LOCK(skb) (((isdn_audio_data_t *)&skb->cb[0])->lock) | ||
286 | #endif | ||
287 | |||
288 | /* Private data of AT-command-interpreter */ | ||
289 | typedef struct atemu { | ||
290 | u_char profile[ISDN_MODEM_NUMREG]; /* Modem-Regs. Profile 0 */ | ||
291 | u_char mdmreg[ISDN_MODEM_NUMREG]; /* Modem-Registers */ | ||
292 | char pmsn[ISDN_MSNLEN]; /* EAZ/MSNs Profile 0 */ | ||
293 | char msn[ISDN_MSNLEN]; /* EAZ/MSN */ | ||
294 | char plmsn[ISDN_LMSNLEN]; /* Listening MSNs Profile 0 */ | ||
295 | char lmsn[ISDN_LMSNLEN]; /* Listening MSNs */ | ||
296 | char cpn[ISDN_MSNLEN]; /* CalledPartyNumber on incoming call */ | ||
297 | char connmsg[ISDN_CMSGLEN]; /* CONNECT-Msg from HL-Driver */ | ||
298 | #ifdef CONFIG_ISDN_AUDIO | ||
299 | u_char vpar[10]; /* Voice-parameters */ | ||
300 | int lastDLE; /* Flag for voice-coding: DLE seen */ | ||
301 | #endif | ||
302 | int mdmcmdl; /* Length of Modem-Commandbuffer */ | ||
303 | int pluscount; /* Counter for +++ sequence */ | ||
304 | u_long lastplus; /* Timestamp of last + */ | ||
305 | int carrierwait; /* Seconds of carrier waiting */ | ||
306 | char mdmcmd[255]; /* Modem-Commandbuffer */ | ||
307 | unsigned int charge; /* Charge units of current connection */ | ||
308 | } atemu; | ||
309 | |||
310 | /* Private data (similar to async_struct in <linux/serial.h>) */ | ||
311 | typedef struct modem_info { | ||
312 | int magic; | ||
313 | struct tty_port port; | ||
314 | int x_char; /* xon/xoff character */ | ||
315 | int mcr; /* Modem control register */ | ||
316 | int msr; /* Modem status register */ | ||
317 | int lsr; /* Line status register */ | ||
318 | int line; | ||
319 | int online; /* 1 = B-Channel is up, drop data */ | ||
320 | /* 2 = B-Channel is up, deliver d.*/ | ||
321 | int dialing; /* Dial in progress or ATA */ | ||
322 | int closing; | ||
323 | int rcvsched; /* Receive needs schedule */ | ||
324 | int isdn_driver; /* Index to isdn-driver */ | ||
325 | int isdn_channel; /* Index to isdn-channel */ | ||
326 | int drv_index; /* Index to dev->usage */ | ||
327 | int ncarrier; /* Flag: schedule NO CARRIER */ | ||
328 | unsigned char last_cause[8]; /* Last cause message */ | ||
329 | unsigned char last_num[ISDN_MSNLEN]; | ||
330 | /* Last phone-number */ | ||
331 | unsigned char last_l2; /* Last layer-2 protocol */ | ||
332 | unsigned char last_si; /* Last service */ | ||
333 | unsigned char last_lhup; /* Last hangup local? */ | ||
334 | unsigned char last_dir; /* Last direction (in or out) */ | ||
335 | struct timer_list nc_timer; /* Timer for delayed NO CARRIER */ | ||
336 | int send_outstanding;/* # of outstanding send-requests */ | ||
337 | int xmit_size; /* max. # of chars in xmit_buf */ | ||
338 | int xmit_count; /* # of chars in xmit_buf */ | ||
339 | struct sk_buff_head xmit_queue; /* transmit queue */ | ||
340 | atomic_t xmit_lock; /* Semaphore for isdn_tty_write */ | ||
341 | #ifdef CONFIG_ISDN_AUDIO | ||
342 | int vonline; /* Voice-channel status */ | ||
343 | /* Bit 0 = recording */ | ||
344 | /* Bit 1 = playback */ | ||
345 | /* Bit 2 = playback, DLE-ETX seen */ | ||
346 | struct sk_buff_head dtmf_queue; /* queue for dtmf results */ | ||
347 | void *adpcms; /* state for adpcm decompression */ | ||
348 | void *adpcmr; /* state for adpcm compression */ | ||
349 | void *dtmf_state; /* state for dtmf decoder */ | ||
350 | void *silence_state; /* state for silence detection */ | ||
351 | #endif | ||
352 | #ifdef CONFIG_ISDN_TTY_FAX | ||
353 | struct T30_s *fax; /* T30 Fax Group 3 data/interface */ | ||
354 | int faxonline; /* Fax-channel status */ | ||
355 | #endif | ||
356 | atemu emu; /* AT-emulator data */ | ||
357 | spinlock_t readlock; | ||
358 | } modem_info; | ||
359 | |||
360 | #define ISDN_MODEM_WINSIZE 8 | ||
361 | |||
362 | /* Description of one ISDN-tty */ | ||
363 | typedef struct _isdn_modem { | ||
364 | int refcount; /* Number of opens */ | ||
365 | struct tty_driver *tty_modem; /* tty-device */ | ||
366 | struct tty_struct *modem_table[ISDN_MAX_CHANNELS]; /* ?? copied from Orig */ | ||
367 | struct ktermios *modem_termios[ISDN_MAX_CHANNELS]; | ||
368 | struct ktermios *modem_termios_locked[ISDN_MAX_CHANNELS]; | ||
369 | modem_info info[ISDN_MAX_CHANNELS]; /* Private data */ | ||
370 | } isdn_modem_t; | ||
371 | |||
372 | /*======================= End of ISDN-tty stuff ============================*/ | ||
373 | |||
374 | /*======================== Start of V.110 stuff ============================*/ | ||
375 | #define V110_BUFSIZE 1024 | ||
376 | |||
377 | typedef struct { | ||
378 | int nbytes; /* 1 Matrixbyte -> nbytes in stream */ | ||
379 | int nbits; /* Number of used bits in streambyte */ | ||
380 | unsigned char key; /* Bitmask in stream eg. 11 (nbits=2) */ | ||
381 | int decodelen; /* Amount of data in decodebuf */ | ||
382 | int SyncInit; /* Number of sync frames to send */ | ||
383 | unsigned char *OnlineFrame; /* Precalculated V110 idle frame */ | ||
384 | unsigned char *OfflineFrame; /* Precalculated V110 sync Frame */ | ||
385 | int framelen; /* Length of frames */ | ||
386 | int skbuser; /* Number of unacked userdata skbs */ | ||
387 | int skbidle; /* Number of unacked idle/sync skbs */ | ||
388 | int introducer; /* Local vars for decoder */ | ||
389 | int dbit; | ||
390 | unsigned char b; | ||
391 | int skbres; /* space to reserve in outgoing skb */ | ||
392 | int maxsize; /* maxbufsize of lowlevel driver */ | ||
393 | unsigned char *encodebuf; /* temporary buffer for encoding */ | ||
394 | unsigned char decodebuf[V110_BUFSIZE]; /* incomplete V110 matrices */ | ||
395 | } isdn_v110_stream; | ||
396 | |||
397 | /*========================= End of V.110 stuff =============================*/ | ||
398 | |||
399 | /*======================= Start of general stuff ===========================*/ | ||
400 | |||
401 | typedef struct { | ||
402 | char *next; | ||
403 | char *private; | ||
404 | } infostruct; | ||
405 | |||
406 | #define DRV_FLAG_RUNNING 1 | ||
407 | #define DRV_FLAG_REJBUS 2 | ||
408 | #define DRV_FLAG_LOADED 4 | ||
409 | |||
410 | /* Description of hardware-level-driver */ | ||
411 | typedef struct _isdn_driver { | ||
412 | ulong online; /* Channel-Online flags */ | ||
413 | ulong flags; /* Misc driver Flags */ | ||
414 | int locks; /* Number of locks for this driver */ | ||
415 | int channels; /* Number of channels */ | ||
416 | wait_queue_head_t st_waitq; /* Wait-Queue for status-read's */ | ||
417 | int maxbufsize; /* Maximum Buffersize supported */ | ||
418 | unsigned long pktcount; /* Until now: unused */ | ||
419 | int stavail; /* Chars avail on Status-device */ | ||
420 | isdn_if *interface; /* Interface to driver */ | ||
421 | int *rcverr; /* Error-counters for B-Ch.-receive */ | ||
422 | int *rcvcount; /* Byte-counters for B-Ch.-receive */ | ||
423 | #ifdef CONFIG_ISDN_AUDIO | ||
424 | unsigned long DLEflag; /* Flags: Insert DLE at next read */ | ||
425 | #endif | ||
426 | struct sk_buff_head *rpqueue; /* Pointers to start of Rcv-Queue */ | ||
427 | wait_queue_head_t *rcv_waitq; /* Wait-Queues for B-Channel-Reads */ | ||
428 | wait_queue_head_t *snd_waitq; /* Wait-Queue for B-Channel-Send's */ | ||
429 | char msn2eaz[10][ISDN_MSNLEN]; /* Mapping-Table MSN->EAZ */ | ||
430 | } isdn_driver_t; | ||
431 | |||
432 | /* Main driver-data */ | ||
433 | typedef struct isdn_devt { | ||
434 | struct module *owner; | ||
435 | spinlock_t lock; | ||
436 | unsigned short flags; /* Bitmapped Flags: */ | ||
437 | int drivers; /* Current number of drivers */ | ||
438 | int channels; /* Current number of channels */ | ||
439 | int net_verbose; /* Verbose-Flag */ | ||
440 | int modempoll; /* Flag: tty-read active */ | ||
441 | spinlock_t timerlock; | ||
442 | int tflags; /* Timer-Flags: */ | ||
443 | /* see ISDN_TIMER_..defines */ | ||
444 | int global_flags; | ||
445 | infostruct *infochain; /* List of open info-devs. */ | ||
446 | wait_queue_head_t info_waitq; /* Wait-Queue for isdninfo */ | ||
447 | struct timer_list timer; /* Misc.-function Timer */ | ||
448 | int chanmap[ISDN_MAX_CHANNELS]; /* Map minor->device-channel */ | ||
449 | int drvmap[ISDN_MAX_CHANNELS]; /* Map minor->driver-index */ | ||
450 | int usage[ISDN_MAX_CHANNELS]; /* Used by tty/ip/voice */ | ||
451 | char num[ISDN_MAX_CHANNELS][ISDN_MSNLEN]; | ||
452 | /* Remote number of active ch.*/ | ||
453 | int m_idx[ISDN_MAX_CHANNELS]; /* Index for mdm.... */ | ||
454 | isdn_driver_t *drv[ISDN_MAX_DRIVERS]; /* Array of drivers */ | ||
455 | isdn_net_dev *netdev; /* Linked list of net-if's */ | ||
456 | char drvid[ISDN_MAX_DRIVERS][20];/* Driver-ID */ | ||
457 | struct task_struct *profd; /* For iprofd */ | ||
458 | isdn_modem_t mdm; /* tty-driver-data */ | ||
459 | isdn_net_dev *rx_netdev[ISDN_MAX_CHANNELS]; /* rx netdev-pointers */ | ||
460 | isdn_net_dev *st_netdev[ISDN_MAX_CHANNELS]; /* stat netdev-pointers */ | ||
461 | ulong ibytes[ISDN_MAX_CHANNELS]; /* Statistics incoming bytes */ | ||
462 | ulong obytes[ISDN_MAX_CHANNELS]; /* Statistics outgoing bytes */ | ||
463 | int v110emu[ISDN_MAX_CHANNELS]; /* V.110 emulator-mode 0=none */ | ||
464 | atomic_t v110use[ISDN_MAX_CHANNELS]; /* Usage-Semaphore for stream */ | ||
465 | isdn_v110_stream *v110[ISDN_MAX_CHANNELS]; /* V.110 private data */ | ||
466 | struct mutex mtx; /* serialize list access*/ | ||
467 | unsigned long global_features; | ||
468 | } isdn_dev; | ||
469 | |||
470 | extern isdn_dev *dev; | ||
471 | |||
472 | |||
473 | #endif /* __ISDN_H__ */ | ||
diff --git a/include/linux/isdn_divertif.h b/include/linux/isdn_divertif.h deleted file mode 100644 index 19ab361f9f07..000000000000 --- a/include/linux/isdn_divertif.h +++ /dev/null | |||
@@ -1,35 +0,0 @@ | |||
1 | /* $Id: isdn_divertif.h,v 1.4.6.1 2001/09/23 22:25:05 kai Exp $ | ||
2 | * | ||
3 | * Header for the diversion supplementary interface for i4l. | ||
4 | * | ||
5 | * Author Werner Cornelius (werner@titro.de) | ||
6 | * Copyright by Werner Cornelius (werner@titro.de) | ||
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 | #ifndef _LINUX_ISDN_DIVERTIF_H | ||
13 | #define _LINUX_ISDN_DIVERTIF_H | ||
14 | |||
15 | #include <linux/isdnif.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <uapi/linux/isdn_divertif.h> | ||
18 | |||
19 | /***************************************************************/ | ||
20 | /* structure exchanging data between isdn hl and divert module */ | ||
21 | /***************************************************************/ | ||
22 | typedef struct | ||
23 | { ulong if_magic; /* magic info and version */ | ||
24 | int cmd; /* command */ | ||
25 | int (*stat_callback)(isdn_ctrl *); /* supplied by divert module when calling */ | ||
26 | int (*ll_cmd)(isdn_ctrl *); /* supplied by hl on return */ | ||
27 | char * (*drv_to_name)(int); /* map a driver id to name, supplied by hl */ | ||
28 | int (*name_to_drv)(char *); /* map a driver id to name, supplied by hl */ | ||
29 | } isdn_divert_if; | ||
30 | |||
31 | /*********************/ | ||
32 | /* function register */ | ||
33 | /*********************/ | ||
34 | extern int DIVERT_REG_NAME(isdn_divert_if *); | ||
35 | #endif /* _LINUX_ISDN_DIVERTIF_H */ | ||
diff --git a/include/linux/isdn_ppp.h b/include/linux/isdn_ppp.h deleted file mode 100644 index a0070c6dfaf8..000000000000 --- a/include/linux/isdn_ppp.h +++ /dev/null | |||
@@ -1,194 +0,0 @@ | |||
1 | /* Linux ISDN subsystem, sync PPP, interface to ipppd | ||
2 | * | ||
3 | * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de) | ||
4 | * Copyright 1995,96 Thinking Objects Software GmbH Wuerzburg | ||
5 | * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) | ||
6 | * Copyright 2000-2002 by Kai Germaschewski (kai@germaschewski.name) | ||
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 | #ifndef _LINUX_ISDN_PPP_H | ||
13 | #define _LINUX_ISDN_PPP_H | ||
14 | |||
15 | |||
16 | |||
17 | |||
18 | #ifdef CONFIG_IPPP_FILTER | ||
19 | #include <linux/filter.h> | ||
20 | #endif | ||
21 | #include <uapi/linux/isdn_ppp.h> | ||
22 | |||
23 | #define DECOMP_ERR_NOMEM (-10) | ||
24 | |||
25 | #define MP_END_FRAG 0x40 | ||
26 | #define MP_BEGIN_FRAG 0x80 | ||
27 | |||
28 | #define MP_MAX_QUEUE_LEN 16 | ||
29 | |||
30 | /* | ||
31 | * We need a way for the decompressor to influence the generation of CCP | ||
32 | * Reset-Requests in a variety of ways. The decompressor is already returning | ||
33 | * a lot of information (generated skb length, error conditions) so we use | ||
34 | * another parameter. This parameter is a pointer to a structure which is | ||
35 | * to be marked valid by the decompressor and only in this case is ever used. | ||
36 | * Furthermore, the only case where this data is used is when the decom- | ||
37 | * pressor returns DECOMP_ERROR. | ||
38 | * | ||
39 | * We use this same struct for the reset entry of the compressor to commu- | ||
40 | * nicate to its caller how to deal with sending of a Reset Ack. In this | ||
41 | * case, expra is not used, but other options still apply (suppressing | ||
42 | * sending with rsend, appending arbitrary data, etc). | ||
43 | */ | ||
44 | |||
45 | #define IPPP_RESET_MAXDATABYTES 32 | ||
46 | |||
47 | struct isdn_ppp_resetparams { | ||
48 | unsigned char valid:1; /* rw Is this structure filled at all ? */ | ||
49 | unsigned char rsend:1; /* rw Should we send one at all ? */ | ||
50 | unsigned char idval:1; /* rw Is the id field valid ? */ | ||
51 | unsigned char dtval:1; /* rw Is the data field valid ? */ | ||
52 | unsigned char expra:1; /* rw Is an Ack expected for this Req ? */ | ||
53 | unsigned char id; /* wo Send CCP ResetReq with this id */ | ||
54 | unsigned short maxdlen; /* ro Max bytes to be stored in data field */ | ||
55 | unsigned short dlen; /* rw Bytes stored in data field */ | ||
56 | unsigned char *data; /* wo Data for ResetReq info field */ | ||
57 | }; | ||
58 | |||
59 | /* | ||
60 | * this is an 'old friend' from ppp-comp.h under a new name | ||
61 | * check the original include for more information | ||
62 | */ | ||
63 | struct isdn_ppp_compressor { | ||
64 | struct isdn_ppp_compressor *next, *prev; | ||
65 | struct module *owner; | ||
66 | int num; /* CCP compression protocol number */ | ||
67 | |||
68 | void *(*alloc) (struct isdn_ppp_comp_data *); | ||
69 | void (*free) (void *state); | ||
70 | int (*init) (void *state, struct isdn_ppp_comp_data *, | ||
71 | int unit,int debug); | ||
72 | |||
73 | /* The reset entry needs to get more exact information about the | ||
74 | ResetReq or ResetAck it was called with. The parameters are | ||
75 | obvious. If reset is called without a Req or Ack frame which | ||
76 | could be handed into it, code MUST be set to 0. Using rsparm, | ||
77 | the reset entry can control if and how a ResetAck is returned. */ | ||
78 | |||
79 | void (*reset) (void *state, unsigned char code, unsigned char id, | ||
80 | unsigned char *data, unsigned len, | ||
81 | struct isdn_ppp_resetparams *rsparm); | ||
82 | |||
83 | int (*compress) (void *state, struct sk_buff *in, | ||
84 | struct sk_buff *skb_out, int proto); | ||
85 | |||
86 | int (*decompress) (void *state,struct sk_buff *in, | ||
87 | struct sk_buff *skb_out, | ||
88 | struct isdn_ppp_resetparams *rsparm); | ||
89 | |||
90 | void (*incomp) (void *state, struct sk_buff *in,int proto); | ||
91 | void (*stat) (void *state, struct compstat *stats); | ||
92 | }; | ||
93 | |||
94 | extern int isdn_ppp_register_compressor(struct isdn_ppp_compressor *); | ||
95 | extern int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *); | ||
96 | extern int isdn_ppp_dial_slave(char *); | ||
97 | extern int isdn_ppp_hangup_slave(char *); | ||
98 | |||
99 | typedef struct { | ||
100 | unsigned long seqerrs; | ||
101 | unsigned long frame_drops; | ||
102 | unsigned long overflows; | ||
103 | unsigned long max_queue_len; | ||
104 | } isdn_mppp_stats; | ||
105 | |||
106 | typedef struct { | ||
107 | int mp_mrru; /* unused */ | ||
108 | struct sk_buff * frags; /* fragments sl list -- use skb->next */ | ||
109 | long frames; /* number of frames in the frame list */ | ||
110 | unsigned int seq; /* last processed packet seq #: any packets | ||
111 | * with smaller seq # will be dropped | ||
112 | * unconditionally */ | ||
113 | spinlock_t lock; | ||
114 | int ref_ct; | ||
115 | /* statistics */ | ||
116 | isdn_mppp_stats stats; | ||
117 | } ippp_bundle; | ||
118 | |||
119 | #define NUM_RCV_BUFFS 64 | ||
120 | |||
121 | struct ippp_buf_queue { | ||
122 | struct ippp_buf_queue *next; | ||
123 | struct ippp_buf_queue *last; | ||
124 | char *buf; /* NULL here indicates end of queue */ | ||
125 | int len; | ||
126 | }; | ||
127 | |||
128 | /* The data structure for one CCP reset transaction */ | ||
129 | enum ippp_ccp_reset_states { | ||
130 | CCPResetIdle, | ||
131 | CCPResetSentReq, | ||
132 | CCPResetRcvdReq, | ||
133 | CCPResetSentAck, | ||
134 | CCPResetRcvdAck | ||
135 | }; | ||
136 | |||
137 | struct ippp_ccp_reset_state { | ||
138 | enum ippp_ccp_reset_states state; /* State of this transaction */ | ||
139 | struct ippp_struct *is; /* Backlink to device stuff */ | ||
140 | unsigned char id; /* Backlink id index */ | ||
141 | unsigned char ta:1; /* The timer is active (flag) */ | ||
142 | unsigned char expra:1; /* We expect a ResetAck at all */ | ||
143 | int dlen; /* Databytes stored in data */ | ||
144 | struct timer_list timer; /* For timeouts/retries */ | ||
145 | /* This is a hack but seems sufficient for the moment. We do not want | ||
146 | to have this be yet another allocation for some bytes, it is more | ||
147 | memory management overhead than the whole mess is worth. */ | ||
148 | unsigned char data[IPPP_RESET_MAXDATABYTES]; | ||
149 | }; | ||
150 | |||
151 | /* The data structure keeping track of the currently outstanding CCP Reset | ||
152 | transactions. */ | ||
153 | struct ippp_ccp_reset { | ||
154 | struct ippp_ccp_reset_state *rs[256]; /* One per possible id */ | ||
155 | unsigned char lastid; /* Last id allocated by the engine */ | ||
156 | }; | ||
157 | |||
158 | struct ippp_struct { | ||
159 | struct ippp_struct *next_link; | ||
160 | int state; | ||
161 | spinlock_t buflock; | ||
162 | struct ippp_buf_queue rq[NUM_RCV_BUFFS]; /* packet queue for isdn_ppp_read() */ | ||
163 | struct ippp_buf_queue *first; /* pointer to (current) first packet */ | ||
164 | struct ippp_buf_queue *last; /* pointer to (current) last used packet in queue */ | ||
165 | wait_queue_head_t wq; | ||
166 | struct task_struct *tk; | ||
167 | unsigned int mpppcfg; | ||
168 | unsigned int pppcfg; | ||
169 | unsigned int mru; | ||
170 | unsigned int mpmru; | ||
171 | unsigned int mpmtu; | ||
172 | unsigned int maxcid; | ||
173 | struct isdn_net_local_s *lp; | ||
174 | int unit; | ||
175 | int minor; | ||
176 | unsigned int last_link_seqno; | ||
177 | long mp_seqno; | ||
178 | #ifdef CONFIG_ISDN_PPP_VJ | ||
179 | unsigned char *cbuf; | ||
180 | struct slcompress *slcomp; | ||
181 | #endif | ||
182 | #ifdef CONFIG_IPPP_FILTER | ||
183 | struct bpf_prog *pass_filter; /* filter for packets to pass */ | ||
184 | struct bpf_prog *active_filter; /* filter for pkts to reset idle */ | ||
185 | #endif | ||
186 | unsigned long debug; | ||
187 | struct isdn_ppp_compressor *compressor,*decompressor; | ||
188 | struct isdn_ppp_compressor *link_compressor,*link_decompressor; | ||
189 | void *decomp_stat,*comp_stat,*link_decomp_stat,*link_comp_stat; | ||
190 | struct ippp_ccp_reset *reset; /* Allocated on demand, may never be needed */ | ||
191 | unsigned long compflags; | ||
192 | }; | ||
193 | |||
194 | #endif /* _LINUX_ISDN_PPP_H */ | ||
diff --git a/include/linux/isdnif.h b/include/linux/isdnif.h deleted file mode 100644 index 8d80fdc68647..000000000000 --- a/include/linux/isdnif.h +++ /dev/null | |||
@@ -1,505 +0,0 @@ | |||
1 | /* $Id: isdnif.h,v 1.43.2.2 2004/01/12 23:08:35 keil Exp $ | ||
2 | * | ||
3 | * Linux ISDN subsystem | ||
4 | * Definition of the interface between the subsystem and its low-level drivers. | ||
5 | * | ||
6 | * Copyright 1994,95,96 by Fritz Elfert (fritz@isdn4linux.de) | ||
7 | * Copyright 1995,96 Thinking Objects Software GmbH Wuerzburg | ||
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 | #ifndef __ISDNIF_H__ | ||
14 | #define __ISDNIF_H__ | ||
15 | |||
16 | |||
17 | #include <linux/skbuff.h> | ||
18 | #include <uapi/linux/isdnif.h> | ||
19 | |||
20 | /***************************************************************************/ | ||
21 | /* Extensions made by Werner Cornelius (werner@ikt.de) */ | ||
22 | /* */ | ||
23 | /* The proceed command holds a incoming call in a state to leave processes */ | ||
24 | /* enough time to check whether ist should be accepted. */ | ||
25 | /* The PROT_IO Command extends the interface to make protocol dependent */ | ||
26 | /* features available (call diversion, call waiting...). */ | ||
27 | /* */ | ||
28 | /* The PROT_IO Command is executed with the desired driver id and the arg */ | ||
29 | /* parameter coded as follows: */ | ||
30 | /* The lower 8 bits of arg contain the desired protocol from ISDN_PTYPE */ | ||
31 | /* definitions. The upper 24 bits represent the protocol specific cmd/stat.*/ | ||
32 | /* Any additional data is protocol and command specific. */ | ||
33 | /* This mechanism also applies to the statcallb callback STAT_PROT. */ | ||
34 | /* */ | ||
35 | /* This suggested extension permits an easy expansion of protocol specific */ | ||
36 | /* handling. Extensions may be added at any time without changing the HL */ | ||
37 | /* driver code and not getting conflicts without certifications. */ | ||
38 | /* The well known CAPI 2.0 interface handles such extensions in a similar */ | ||
39 | /* way. Perhaps a protocol specific module may be added and separately */ | ||
40 | /* loaded and linked to the basic isdn module for handling. */ | ||
41 | /***************************************************************************/ | ||
42 | |||
43 | /*****************/ | ||
44 | /* DSS1 commands */ | ||
45 | /*****************/ | ||
46 | #define DSS1_CMD_INVOKE ((0x00 << 8) | ISDN_PTYPE_EURO) /* invoke a supplementary service */ | ||
47 | #define DSS1_CMD_INVOKE_ABORT ((0x01 << 8) | ISDN_PTYPE_EURO) /* abort a invoke cmd */ | ||
48 | |||
49 | /*******************************/ | ||
50 | /* DSS1 Status callback values */ | ||
51 | /*******************************/ | ||
52 | #define DSS1_STAT_INVOKE_RES ((0x80 << 8) | ISDN_PTYPE_EURO) /* Result for invocation */ | ||
53 | #define DSS1_STAT_INVOKE_ERR ((0x81 << 8) | ISDN_PTYPE_EURO) /* Error Return for invocation */ | ||
54 | #define DSS1_STAT_INVOKE_BRD ((0x82 << 8) | ISDN_PTYPE_EURO) /* Deliver invoke broadcast info */ | ||
55 | |||
56 | |||
57 | /*********************************************************************/ | ||
58 | /* structures for DSS1 commands and callback */ | ||
59 | /* */ | ||
60 | /* An action is invoked by sending a DSS1_CMD_INVOKE. The ll_id, proc*/ | ||
61 | /* timeout, datalen and data fields must be set before calling. */ | ||
62 | /* */ | ||
63 | /* The return value is a positive hl_id value also delivered in the */ | ||
64 | /* hl_id field. A value of zero signals no more left hl_id capacitys.*/ | ||
65 | /* A negative return value signals errors in LL. So if the return */ | ||
66 | /* value is <= 0 no action in LL will be taken -> request ignored */ | ||
67 | /* */ | ||
68 | /* The timeout field must be filled with a positive value specifying */ | ||
69 | /* the amount of time the INVOKED process waits for a reaction from */ | ||
70 | /* the network. */ | ||
71 | /* If a response (either error or result) is received during this */ | ||
72 | /* intervall, a reporting callback is initiated and the process will */ | ||
73 | /* be deleted, the hl identifier will be freed. */ | ||
74 | /* If no response is received during the specified intervall, a error*/ | ||
75 | /* callback is initiated with timeout set to -1 and a datalen set */ | ||
76 | /* to 0. */ | ||
77 | /* If timeout is set to a value <= 0 during INVOCATION the process is*/ | ||
78 | /* immediately deleted after sending the data. No callback occurs ! */ | ||
79 | /* */ | ||
80 | /* A currently waiting process may be aborted with INVOKE_ABORT. No */ | ||
81 | /* callback will occur when a process has been aborted. */ | ||
82 | /* */ | ||
83 | /* Broadcast invoke frames from the network are reported via the */ | ||
84 | /* STAT_INVOKE_BRD callback. The ll_id is set to 0, the other fields */ | ||
85 | /* are supplied by the network and not by the HL. */ | ||
86 | /*********************************************************************/ | ||
87 | |||
88 | /*****************/ | ||
89 | /* NI1 commands */ | ||
90 | /*****************/ | ||
91 | #define NI1_CMD_INVOKE ((0x00 << 8) | ISDN_PTYPE_NI1) /* invoke a supplementary service */ | ||
92 | #define NI1_CMD_INVOKE_ABORT ((0x01 << 8) | ISDN_PTYPE_NI1) /* abort a invoke cmd */ | ||
93 | |||
94 | /*******************************/ | ||
95 | /* NI1 Status callback values */ | ||
96 | /*******************************/ | ||
97 | #define NI1_STAT_INVOKE_RES ((0x80 << 8) | ISDN_PTYPE_NI1) /* Result for invocation */ | ||
98 | #define NI1_STAT_INVOKE_ERR ((0x81 << 8) | ISDN_PTYPE_NI1) /* Error Return for invocation */ | ||
99 | #define NI1_STAT_INVOKE_BRD ((0x82 << 8) | ISDN_PTYPE_NI1) /* Deliver invoke broadcast info */ | ||
100 | |||
101 | typedef struct | ||
102 | { ulong ll_id; /* ID supplied by LL when executing */ | ||
103 | /* a command and returned by HL for */ | ||
104 | /* INVOKE_RES and INVOKE_ERR */ | ||
105 | int hl_id; /* ID supplied by HL when called */ | ||
106 | /* for executing a cmd and delivered */ | ||
107 | /* for results and errors */ | ||
108 | /* must be supplied by LL when aborting*/ | ||
109 | int proc; /* invoke procedure used by CMD_INVOKE */ | ||
110 | /* returned by callback and broadcast */ | ||
111 | int timeout; /* timeout for INVOKE CMD in ms */ | ||
112 | /* -1 in stat callback when timed out */ | ||
113 | /* error value when error callback */ | ||
114 | int datalen; /* length of cmd or stat data */ | ||
115 | u_char *data;/* pointer to data delivered or send */ | ||
116 | } isdn_cmd_stat; | ||
117 | |||
118 | /* | ||
119 | * Commands from linklevel to lowlevel | ||
120 | * | ||
121 | */ | ||
122 | #define ISDN_CMD_IOCTL 0 /* Perform ioctl */ | ||
123 | #define ISDN_CMD_DIAL 1 /* Dial out */ | ||
124 | #define ISDN_CMD_ACCEPTD 2 /* Accept an incoming call on D-Chan. */ | ||
125 | #define ISDN_CMD_ACCEPTB 3 /* Request B-Channel connect. */ | ||
126 | #define ISDN_CMD_HANGUP 4 /* Hangup */ | ||
127 | #define ISDN_CMD_CLREAZ 5 /* Clear EAZ(s) of channel */ | ||
128 | #define ISDN_CMD_SETEAZ 6 /* Set EAZ(s) of channel */ | ||
129 | #define ISDN_CMD_GETEAZ 7 /* Get EAZ(s) of channel */ | ||
130 | #define ISDN_CMD_SETSIL 8 /* Set Service-Indicator-List of channel */ | ||
131 | #define ISDN_CMD_GETSIL 9 /* Get Service-Indicator-List of channel */ | ||
132 | #define ISDN_CMD_SETL2 10 /* Set B-Chan. Layer2-Parameter */ | ||
133 | #define ISDN_CMD_GETL2 11 /* Get B-Chan. Layer2-Parameter */ | ||
134 | #define ISDN_CMD_SETL3 12 /* Set B-Chan. Layer3-Parameter */ | ||
135 | #define ISDN_CMD_GETL3 13 /* Get B-Chan. Layer3-Parameter */ | ||
136 | // #define ISDN_CMD_LOCK 14 /* Signal usage by upper levels */ | ||
137 | // #define ISDN_CMD_UNLOCK 15 /* Release usage-lock */ | ||
138 | #define ISDN_CMD_SUSPEND 16 /* Suspend connection */ | ||
139 | #define ISDN_CMD_RESUME 17 /* Resume connection */ | ||
140 | #define ISDN_CMD_PROCEED 18 /* Proceed with call establishment */ | ||
141 | #define ISDN_CMD_ALERT 19 /* Alert after Proceeding */ | ||
142 | #define ISDN_CMD_REDIR 20 /* Redir a incoming call */ | ||
143 | #define ISDN_CMD_PROT_IO 21 /* Protocol specific commands */ | ||
144 | #define CAPI_PUT_MESSAGE 22 /* CAPI message send down or up */ | ||
145 | #define ISDN_CMD_FAXCMD 23 /* FAX commands to HL-driver */ | ||
146 | #define ISDN_CMD_AUDIO 24 /* DSP, DTMF, ... settings */ | ||
147 | |||
148 | /* | ||
149 | * Status-Values delivered from lowlevel to linklevel via | ||
150 | * statcallb(). | ||
151 | * | ||
152 | */ | ||
153 | #define ISDN_STAT_STAVAIL 256 /* Raw status-data available */ | ||
154 | #define ISDN_STAT_ICALL 257 /* Incoming call detected */ | ||
155 | #define ISDN_STAT_RUN 258 /* Signal protocol-code is running */ | ||
156 | #define ISDN_STAT_STOP 259 /* Signal halt of protocol-code */ | ||
157 | #define ISDN_STAT_DCONN 260 /* Signal D-Channel connect */ | ||
158 | #define ISDN_STAT_BCONN 261 /* Signal B-Channel connect */ | ||
159 | #define ISDN_STAT_DHUP 262 /* Signal D-Channel disconnect */ | ||
160 | #define ISDN_STAT_BHUP 263 /* Signal B-Channel disconnect */ | ||
161 | #define ISDN_STAT_CINF 264 /* Charge-Info */ | ||
162 | #define ISDN_STAT_LOAD 265 /* Signal new lowlevel-driver is loaded */ | ||
163 | #define ISDN_STAT_UNLOAD 266 /* Signal unload of lowlevel-driver */ | ||
164 | #define ISDN_STAT_BSENT 267 /* Signal packet sent */ | ||
165 | #define ISDN_STAT_NODCH 268 /* Signal no D-Channel */ | ||
166 | #define ISDN_STAT_ADDCH 269 /* Add more Channels */ | ||
167 | #define ISDN_STAT_CAUSE 270 /* Cause-Message */ | ||
168 | #define ISDN_STAT_ICALLW 271 /* Incoming call without B-chan waiting */ | ||
169 | #define ISDN_STAT_REDIR 272 /* Redir result */ | ||
170 | #define ISDN_STAT_PROT 273 /* protocol IO specific callback */ | ||
171 | #define ISDN_STAT_DISPLAY 274 /* deliver a received display message */ | ||
172 | #define ISDN_STAT_L1ERR 275 /* Signal Layer-1 Error */ | ||
173 | #define ISDN_STAT_FAXIND 276 /* FAX indications from HL-driver */ | ||
174 | #define ISDN_STAT_AUDIO 277 /* DTMF, DSP indications */ | ||
175 | #define ISDN_STAT_DISCH 278 /* Disable/Enable channel usage */ | ||
176 | |||
177 | /* | ||
178 | * Audio commands | ||
179 | */ | ||
180 | #define ISDN_AUDIO_SETDD 0 /* Set DTMF detection */ | ||
181 | #define ISDN_AUDIO_DTMF 1 /* Rx/Tx DTMF */ | ||
182 | |||
183 | /* | ||
184 | * Values for errcode field | ||
185 | */ | ||
186 | #define ISDN_STAT_L1ERR_SEND 1 | ||
187 | #define ISDN_STAT_L1ERR_RECV 2 | ||
188 | |||
189 | /* | ||
190 | * Values for feature-field of interface-struct. | ||
191 | */ | ||
192 | /* Layer 2 */ | ||
193 | #define ISDN_FEATURE_L2_X75I (0x0001 << ISDN_PROTO_L2_X75I) | ||
194 | #define ISDN_FEATURE_L2_X75UI (0x0001 << ISDN_PROTO_L2_X75UI) | ||
195 | #define ISDN_FEATURE_L2_X75BUI (0x0001 << ISDN_PROTO_L2_X75BUI) | ||
196 | #define ISDN_FEATURE_L2_HDLC (0x0001 << ISDN_PROTO_L2_HDLC) | ||
197 | #define ISDN_FEATURE_L2_TRANS (0x0001 << ISDN_PROTO_L2_TRANS) | ||
198 | #define ISDN_FEATURE_L2_X25DTE (0x0001 << ISDN_PROTO_L2_X25DTE) | ||
199 | #define ISDN_FEATURE_L2_X25DCE (0x0001 << ISDN_PROTO_L2_X25DCE) | ||
200 | #define ISDN_FEATURE_L2_V11096 (0x0001 << ISDN_PROTO_L2_V11096) | ||
201 | #define ISDN_FEATURE_L2_V11019 (0x0001 << ISDN_PROTO_L2_V11019) | ||
202 | #define ISDN_FEATURE_L2_V11038 (0x0001 << ISDN_PROTO_L2_V11038) | ||
203 | #define ISDN_FEATURE_L2_MODEM (0x0001 << ISDN_PROTO_L2_MODEM) | ||
204 | #define ISDN_FEATURE_L2_FAX (0x0001 << ISDN_PROTO_L2_FAX) | ||
205 | #define ISDN_FEATURE_L2_HDLC_56K (0x0001 << ISDN_PROTO_L2_HDLC_56K) | ||
206 | |||
207 | #define ISDN_FEATURE_L2_MASK (0x0FFFF) /* Max. 16 protocols */ | ||
208 | #define ISDN_FEATURE_L2_SHIFT (0) | ||
209 | |||
210 | /* Layer 3 */ | ||
211 | #define ISDN_FEATURE_L3_TRANS (0x10000 << ISDN_PROTO_L3_TRANS) | ||
212 | #define ISDN_FEATURE_L3_TRANSDSP (0x10000 << ISDN_PROTO_L3_TRANSDSP) | ||
213 | #define ISDN_FEATURE_L3_FCLASS2 (0x10000 << ISDN_PROTO_L3_FCLASS2) | ||
214 | #define ISDN_FEATURE_L3_FCLASS1 (0x10000 << ISDN_PROTO_L3_FCLASS1) | ||
215 | |||
216 | #define ISDN_FEATURE_L3_MASK (0x0FF0000) /* Max. 8 Protocols */ | ||
217 | #define ISDN_FEATURE_L3_SHIFT (16) | ||
218 | |||
219 | /* Signaling */ | ||
220 | #define ISDN_FEATURE_P_UNKNOWN (0x1000000 << ISDN_PTYPE_UNKNOWN) | ||
221 | #define ISDN_FEATURE_P_1TR6 (0x1000000 << ISDN_PTYPE_1TR6) | ||
222 | #define ISDN_FEATURE_P_EURO (0x1000000 << ISDN_PTYPE_EURO) | ||
223 | #define ISDN_FEATURE_P_NI1 (0x1000000 << ISDN_PTYPE_NI1) | ||
224 | |||
225 | #define ISDN_FEATURE_P_MASK (0x0FF000000) /* Max. 8 Protocols */ | ||
226 | #define ISDN_FEATURE_P_SHIFT (24) | ||
227 | |||
228 | typedef struct setup_parm { | ||
229 | unsigned char phone[32]; /* Remote Phone-Number */ | ||
230 | unsigned char eazmsn[32]; /* Local EAZ or MSN */ | ||
231 | unsigned char si1; /* Service Indicator 1 */ | ||
232 | unsigned char si2; /* Service Indicator 2 */ | ||
233 | unsigned char plan; /* Numbering plan */ | ||
234 | unsigned char screen; /* Screening info */ | ||
235 | } setup_parm; | ||
236 | |||
237 | |||
238 | #ifdef CONFIG_ISDN_TTY_FAX | ||
239 | /* T.30 Fax G3 */ | ||
240 | |||
241 | #define FAXIDLEN 21 | ||
242 | |||
243 | typedef struct T30_s { | ||
244 | /* session parameters */ | ||
245 | __u8 resolution; | ||
246 | __u8 rate; | ||
247 | __u8 width; | ||
248 | __u8 length; | ||
249 | __u8 compression; | ||
250 | __u8 ecm; | ||
251 | __u8 binary; | ||
252 | __u8 scantime; | ||
253 | __u8 id[FAXIDLEN]; | ||
254 | /* additional parameters */ | ||
255 | __u8 phase; | ||
256 | __u8 direction; | ||
257 | __u8 code; | ||
258 | __u8 badlin; | ||
259 | __u8 badmul; | ||
260 | __u8 bor; | ||
261 | __u8 fet; | ||
262 | __u8 pollid[FAXIDLEN]; | ||
263 | __u8 cq; | ||
264 | __u8 cr; | ||
265 | __u8 ctcrty; | ||
266 | __u8 minsp; | ||
267 | __u8 phcto; | ||
268 | __u8 rel; | ||
269 | __u8 nbc; | ||
270 | /* remote station parameters */ | ||
271 | __u8 r_resolution; | ||
272 | __u8 r_rate; | ||
273 | __u8 r_width; | ||
274 | __u8 r_length; | ||
275 | __u8 r_compression; | ||
276 | __u8 r_ecm; | ||
277 | __u8 r_binary; | ||
278 | __u8 r_scantime; | ||
279 | __u8 r_id[FAXIDLEN]; | ||
280 | __u8 r_code; | ||
281 | } __packed T30_s; | ||
282 | |||
283 | #define ISDN_TTY_FAX_CONN_IN 0 | ||
284 | #define ISDN_TTY_FAX_CONN_OUT 1 | ||
285 | |||
286 | #define ISDN_TTY_FAX_FCON 0 | ||
287 | #define ISDN_TTY_FAX_DIS 1 | ||
288 | #define ISDN_TTY_FAX_FTT 2 | ||
289 | #define ISDN_TTY_FAX_MCF 3 | ||
290 | #define ISDN_TTY_FAX_DCS 4 | ||
291 | #define ISDN_TTY_FAX_TRAIN_OK 5 | ||
292 | #define ISDN_TTY_FAX_EOP 6 | ||
293 | #define ISDN_TTY_FAX_EOM 7 | ||
294 | #define ISDN_TTY_FAX_MPS 8 | ||
295 | #define ISDN_TTY_FAX_DTC 9 | ||
296 | #define ISDN_TTY_FAX_RID 10 | ||
297 | #define ISDN_TTY_FAX_HNG 11 | ||
298 | #define ISDN_TTY_FAX_DT 12 | ||
299 | #define ISDN_TTY_FAX_FCON_I 13 | ||
300 | #define ISDN_TTY_FAX_DR 14 | ||
301 | #define ISDN_TTY_FAX_ET 15 | ||
302 | #define ISDN_TTY_FAX_CFR 16 | ||
303 | #define ISDN_TTY_FAX_PTS 17 | ||
304 | #define ISDN_TTY_FAX_SENT 18 | ||
305 | |||
306 | #define ISDN_FAX_PHASE_IDLE 0 | ||
307 | #define ISDN_FAX_PHASE_A 1 | ||
308 | #define ISDN_FAX_PHASE_B 2 | ||
309 | #define ISDN_FAX_PHASE_C 3 | ||
310 | #define ISDN_FAX_PHASE_D 4 | ||
311 | #define ISDN_FAX_PHASE_E 5 | ||
312 | |||
313 | #endif /* TTY_FAX */ | ||
314 | |||
315 | #define ISDN_FAX_CLASS1_FAE 0 | ||
316 | #define ISDN_FAX_CLASS1_FTS 1 | ||
317 | #define ISDN_FAX_CLASS1_FRS 2 | ||
318 | #define ISDN_FAX_CLASS1_FTM 3 | ||
319 | #define ISDN_FAX_CLASS1_FRM 4 | ||
320 | #define ISDN_FAX_CLASS1_FTH 5 | ||
321 | #define ISDN_FAX_CLASS1_FRH 6 | ||
322 | #define ISDN_FAX_CLASS1_CTRL 7 | ||
323 | |||
324 | #define ISDN_FAX_CLASS1_OK 0 | ||
325 | #define ISDN_FAX_CLASS1_CONNECT 1 | ||
326 | #define ISDN_FAX_CLASS1_NOCARR 2 | ||
327 | #define ISDN_FAX_CLASS1_ERROR 3 | ||
328 | #define ISDN_FAX_CLASS1_FCERROR 4 | ||
329 | #define ISDN_FAX_CLASS1_QUERY 5 | ||
330 | |||
331 | typedef struct { | ||
332 | __u8 cmd; | ||
333 | __u8 subcmd; | ||
334 | __u8 para[50]; | ||
335 | } aux_s; | ||
336 | |||
337 | #define AT_COMMAND 0 | ||
338 | #define AT_EQ_VALUE 1 | ||
339 | #define AT_QUERY 2 | ||
340 | #define AT_EQ_QUERY 3 | ||
341 | |||
342 | /* CAPI structs */ | ||
343 | |||
344 | /* this is compatible to the old union size */ | ||
345 | #define MAX_CAPI_PARA_LEN 50 | ||
346 | |||
347 | typedef struct { | ||
348 | /* Header */ | ||
349 | __u16 Length; | ||
350 | __u16 ApplId; | ||
351 | __u8 Command; | ||
352 | __u8 Subcommand; | ||
353 | __u16 Messagenumber; | ||
354 | |||
355 | /* Parameter */ | ||
356 | union { | ||
357 | __u32 Controller; | ||
358 | __u32 PLCI; | ||
359 | __u32 NCCI; | ||
360 | } adr; | ||
361 | __u8 para[MAX_CAPI_PARA_LEN]; | ||
362 | } capi_msg; | ||
363 | |||
364 | /* | ||
365 | * Structure for exchanging above infos | ||
366 | * | ||
367 | */ | ||
368 | typedef struct { | ||
369 | int driver; /* Lowlevel-Driver-ID */ | ||
370 | int command; /* Command or Status (see above) */ | ||
371 | ulong arg; /* Additional Data */ | ||
372 | union { | ||
373 | ulong errcode; /* Type of error with STAT_L1ERR */ | ||
374 | int length; /* Amount of bytes sent with STAT_BSENT */ | ||
375 | u_char num[50]; /* Additional Data */ | ||
376 | setup_parm setup;/* For SETUP msg */ | ||
377 | capi_msg cmsg; /* For CAPI like messages */ | ||
378 | char display[85];/* display message data */ | ||
379 | isdn_cmd_stat isdn_io; /* ISDN IO-parameter/result */ | ||
380 | aux_s aux; /* for modem commands/indications */ | ||
381 | #ifdef CONFIG_ISDN_TTY_FAX | ||
382 | T30_s *fax; /* Pointer to ttys fax struct */ | ||
383 | #endif | ||
384 | ulong userdata; /* User Data */ | ||
385 | } parm; | ||
386 | } isdn_ctrl; | ||
387 | |||
388 | #define dss1_io isdn_io | ||
389 | #define ni1_io isdn_io | ||
390 | |||
391 | /* | ||
392 | * The interface-struct itself (initialized at load-time of lowlevel-driver) | ||
393 | * | ||
394 | * See Documentation/isdn/INTERFACE for a description, how the communication | ||
395 | * between the ISDN subsystem and its drivers is done. | ||
396 | * | ||
397 | */ | ||
398 | typedef struct { | ||
399 | struct module *owner; | ||
400 | |||
401 | /* Number of channels supported by this driver | ||
402 | */ | ||
403 | int channels; | ||
404 | |||
405 | /* | ||
406 | * Maximum Size of transmit/receive-buffer this driver supports. | ||
407 | */ | ||
408 | int maxbufsize; | ||
409 | |||
410 | /* Feature-Flags for this driver. | ||
411 | * See defines ISDN_FEATURE_... for Values | ||
412 | */ | ||
413 | unsigned long features; | ||
414 | |||
415 | /* | ||
416 | * Needed for calculating | ||
417 | * dev->hard_header_len = linklayer header + hl_hdrlen; | ||
418 | * Drivers, not supporting sk_buff's should set this to 0. | ||
419 | */ | ||
420 | unsigned short hl_hdrlen; | ||
421 | |||
422 | /* | ||
423 | * Receive-Callback using sk_buff's | ||
424 | * Parameters: | ||
425 | * int Driver-ID | ||
426 | * int local channel-number (0 ...) | ||
427 | * struct sk_buff *skb received Data | ||
428 | */ | ||
429 | void (*rcvcallb_skb)(int, int, struct sk_buff *); | ||
430 | |||
431 | /* Status-Callback | ||
432 | * Parameters: | ||
433 | * isdn_ctrl* | ||
434 | * driver = Driver ID. | ||
435 | * command = One of above ISDN_STAT_... constants. | ||
436 | * arg = depending on status-type. | ||
437 | * num = depending on status-type. | ||
438 | */ | ||
439 | int (*statcallb)(isdn_ctrl*); | ||
440 | |||
441 | /* Send command | ||
442 | * Parameters: | ||
443 | * isdn_ctrl* | ||
444 | * driver = Driver ID. | ||
445 | * command = One of above ISDN_CMD_... constants. | ||
446 | * arg = depending on command. | ||
447 | * num = depending on command. | ||
448 | */ | ||
449 | int (*command)(isdn_ctrl*); | ||
450 | |||
451 | /* | ||
452 | * Send data using sk_buff's | ||
453 | * Parameters: | ||
454 | * int driverId | ||
455 | * int local channel-number (0...) | ||
456 | * int Flag: Need ACK for this packet. | ||
457 | * struct sk_buff *skb Data to send | ||
458 | */ | ||
459 | int (*writebuf_skb) (int, int, int, struct sk_buff *); | ||
460 | |||
461 | /* Send raw D-Channel-Commands | ||
462 | * Parameters: | ||
463 | * u_char pointer data | ||
464 | * int length of data | ||
465 | * int driverId | ||
466 | * int local channel-number (0 ...) | ||
467 | */ | ||
468 | int (*writecmd)(const u_char __user *, int, int, int); | ||
469 | |||
470 | /* Read raw Status replies | ||
471 | * u_char pointer data (volatile) | ||
472 | * int length of buffer | ||
473 | * int driverId | ||
474 | * int local channel-number (0 ...) | ||
475 | */ | ||
476 | int (*readstat)(u_char __user *, int, int, int); | ||
477 | |||
478 | char id[20]; | ||
479 | } isdn_if; | ||
480 | |||
481 | /* | ||
482 | * Function which must be called by lowlevel-driver at loadtime with | ||
483 | * the following fields of above struct set: | ||
484 | * | ||
485 | * channels Number of channels that will be supported. | ||
486 | * hl_hdrlen Space to preserve in sk_buff's when sending. Drivers, not | ||
487 | * supporting sk_buff's should set this to 0. | ||
488 | * command Address of Command-Handler. | ||
489 | * features Bitwise coded Features of this driver. (use ISDN_FEATURE_...) | ||
490 | * writebuf_skb Address of Skbuff-Send-Handler. | ||
491 | * writecmd " " D-Channel " which accepts raw D-Ch-Commands. | ||
492 | * readstat " " D-Channel " which delivers raw Status-Data. | ||
493 | * | ||
494 | * The linklevel-driver fills the following fields: | ||
495 | * | ||
496 | * channels Driver-ID assigned to this driver. (Must be used on all | ||
497 | * subsequent callbacks. | ||
498 | * rcvcallb_skb Address of handler for received Skbuff's. | ||
499 | * statcallb " " " for status-changes. | ||
500 | * | ||
501 | */ | ||
502 | extern int register_isdn(isdn_if*); | ||
503 | #include <linux/uaccess.h> | ||
504 | |||
505 | #endif /* __ISDNIF_H__ */ | ||
diff --git a/include/linux/wanrouter.h b/include/linux/wanrouter.h deleted file mode 100644 index f6358558f9f5..000000000000 --- a/include/linux/wanrouter.h +++ /dev/null | |||
@@ -1,11 +0,0 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * wanrouter.h Legacy declarations kept around until X25 is removed | ||
4 | */ | ||
5 | |||
6 | #ifndef _ROUTER_H | ||
7 | #define _ROUTER_H | ||
8 | |||
9 | #include <uapi/linux/wanrouter.h> | ||
10 | |||
11 | #endif /* _ROUTER_H */ | ||
diff --git a/include/uapi/linux/isdn.h b/include/uapi/linux/isdn.h deleted file mode 100644 index f371fd52ed75..000000000000 --- a/include/uapi/linux/isdn.h +++ /dev/null | |||
@@ -1,144 +0,0 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ | ||
2 | /* $Id: isdn.h,v 1.125.2.3 2004/02/10 01:07:14 keil Exp $ | ||
3 | * | ||
4 | * Main header for the Linux ISDN subsystem (linklevel). | ||
5 | * | ||
6 | * Copyright 1994,95,96 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 | #ifndef _UAPI__ISDN_H__ | ||
16 | #define _UAPI__ISDN_H__ | ||
17 | |||
18 | #include <linux/ioctl.h> | ||
19 | #include <linux/tty.h> | ||
20 | |||
21 | #define ISDN_MAX_DRIVERS 32 | ||
22 | #define ISDN_MAX_CHANNELS 64 | ||
23 | |||
24 | /* New ioctl-codes */ | ||
25 | #define IIOCNETAIF _IO('I',1) | ||
26 | #define IIOCNETDIF _IO('I',2) | ||
27 | #define IIOCNETSCF _IO('I',3) | ||
28 | #define IIOCNETGCF _IO('I',4) | ||
29 | #define IIOCNETANM _IO('I',5) | ||
30 | #define IIOCNETDNM _IO('I',6) | ||
31 | #define IIOCNETGNM _IO('I',7) | ||
32 | #define IIOCGETSET _IO('I',8) /* no longer supported */ | ||
33 | #define IIOCSETSET _IO('I',9) /* no longer supported */ | ||
34 | #define IIOCSETVER _IO('I',10) | ||
35 | #define IIOCNETHUP _IO('I',11) | ||
36 | #define IIOCSETGST _IO('I',12) | ||
37 | #define IIOCSETBRJ _IO('I',13) | ||
38 | #define IIOCSIGPRF _IO('I',14) | ||
39 | #define IIOCGETPRF _IO('I',15) | ||
40 | #define IIOCSETPRF _IO('I',16) | ||
41 | #define IIOCGETMAP _IO('I',17) | ||
42 | #define IIOCSETMAP _IO('I',18) | ||
43 | #define IIOCNETASL _IO('I',19) | ||
44 | #define IIOCNETDIL _IO('I',20) | ||
45 | #define IIOCGETCPS _IO('I',21) | ||
46 | #define IIOCGETDVR _IO('I',22) | ||
47 | #define IIOCNETLCR _IO('I',23) /* dwabc ioctl for LCR from isdnlog */ | ||
48 | #define IIOCNETDWRSET _IO('I',24) /* dwabc ioctl to reset abc-values to default on a net-interface */ | ||
49 | |||
50 | #define IIOCNETALN _IO('I',32) | ||
51 | #define IIOCNETDLN _IO('I',33) | ||
52 | |||
53 | #define IIOCNETGPN _IO('I',34) | ||
54 | |||
55 | #define IIOCDBGVAR _IO('I',127) | ||
56 | |||
57 | #define IIOCDRVCTL _IO('I',128) | ||
58 | |||
59 | /* cisco hdlck device private ioctls */ | ||
60 | #define SIOCGKEEPPERIOD (SIOCDEVPRIVATE + 0) | ||
61 | #define SIOCSKEEPPERIOD (SIOCDEVPRIVATE + 1) | ||
62 | #define SIOCGDEBSERINT (SIOCDEVPRIVATE + 2) | ||
63 | #define SIOCSDEBSERINT (SIOCDEVPRIVATE + 3) | ||
64 | |||
65 | /* Packet encapsulations for net-interfaces */ | ||
66 | #define ISDN_NET_ENCAP_ETHER 0 | ||
67 | #define ISDN_NET_ENCAP_RAWIP 1 | ||
68 | #define ISDN_NET_ENCAP_IPTYP 2 | ||
69 | #define ISDN_NET_ENCAP_CISCOHDLC 3 /* Without SLARP and keepalive */ | ||
70 | #define ISDN_NET_ENCAP_SYNCPPP 4 | ||
71 | #define ISDN_NET_ENCAP_UIHDLC 5 | ||
72 | #define ISDN_NET_ENCAP_CISCOHDLCK 6 /* With SLARP and keepalive */ | ||
73 | #define ISDN_NET_ENCAP_X25IFACE 7 /* Documentation/networking/x25-iface.txt */ | ||
74 | #define ISDN_NET_ENCAP_MAX_ENCAP ISDN_NET_ENCAP_X25IFACE | ||
75 | |||
76 | /* Facility which currently uses an ISDN-channel */ | ||
77 | #define ISDN_USAGE_NONE 0 | ||
78 | #define ISDN_USAGE_RAW 1 | ||
79 | #define ISDN_USAGE_MODEM 2 | ||
80 | #define ISDN_USAGE_NET 3 | ||
81 | #define ISDN_USAGE_VOICE 4 | ||
82 | #define ISDN_USAGE_FAX 5 | ||
83 | #define ISDN_USAGE_MASK 7 /* Mask to get plain usage */ | ||
84 | #define ISDN_USAGE_DISABLED 32 /* This bit is set, if channel is disabled */ | ||
85 | #define ISDN_USAGE_EXCLUSIVE 64 /* This bit is set, if channel is exclusive */ | ||
86 | #define ISDN_USAGE_OUTGOING 128 /* This bit is set, if channel is outgoing */ | ||
87 | |||
88 | #define ISDN_MODEM_NUMREG 24 /* Number of Modem-Registers */ | ||
89 | #define ISDN_LMSNLEN 255 /* Length of tty's Listen-MSN string */ | ||
90 | #define ISDN_CMSGLEN 50 /* Length of CONNECT-Message to add for Modem */ | ||
91 | |||
92 | #define ISDN_MSNLEN 32 | ||
93 | #define NET_DV 0x06 /* Data version for isdn_net_ioctl_cfg */ | ||
94 | #define TTY_DV 0x06 /* Data version for iprofd etc. */ | ||
95 | |||
96 | #define INF_DV 0x01 /* Data version for /dev/isdninfo */ | ||
97 | |||
98 | typedef struct { | ||
99 | char drvid[25]; | ||
100 | unsigned long arg; | ||
101 | } isdn_ioctl_struct; | ||
102 | |||
103 | typedef struct { | ||
104 | char name[10]; | ||
105 | char phone[ISDN_MSNLEN]; | ||
106 | int outgoing; | ||
107 | } isdn_net_ioctl_phone; | ||
108 | |||
109 | typedef struct { | ||
110 | char name[10]; /* Name of interface */ | ||
111 | char master[10]; /* Name of Master for Bundling */ | ||
112 | char slave[10]; /* Name of Slave for Bundling */ | ||
113 | char eaz[256]; /* EAZ/MSN */ | ||
114 | char drvid[25]; /* DriverId for Bindings */ | ||
115 | int onhtime; /* Hangup-Timeout */ | ||
116 | int charge; /* Charge-Units */ | ||
117 | int l2_proto; /* Layer-2 protocol */ | ||
118 | int l3_proto; /* Layer-3 protocol */ | ||
119 | int p_encap; /* Encapsulation */ | ||
120 | int exclusive; /* Channel, if bound exclusive */ | ||
121 | int dialmax; /* Dial Retry-Counter */ | ||
122 | int slavedelay; /* Delay until slave starts up */ | ||
123 | int cbdelay; /* Delay before Callback */ | ||
124 | int chargehup; /* Flag: Charge-Hangup */ | ||
125 | int ihup; /* Flag: Hangup-Timeout on incoming line */ | ||
126 | int secure; /* Flag: Secure */ | ||
127 | int callback; /* Flag: Callback */ | ||
128 | int cbhup; /* Flag: Reject Call before Callback */ | ||
129 | int pppbind; /* ippp device for bindings */ | ||
130 | int chargeint; /* Use fixed charge interval length */ | ||
131 | int triggercps; /* BogoCPS needed for triggering slave */ | ||
132 | int dialtimeout; /* Dial-Timeout */ | ||
133 | int dialwait; /* Time to wait after failed dial */ | ||
134 | int dialmode; /* Flag: off / on / auto */ | ||
135 | } isdn_net_ioctl_cfg; | ||
136 | |||
137 | #define ISDN_NET_DIALMODE_MASK 0xC0 /* bits for status */ | ||
138 | #define ISDN_NET_DM_OFF 0x00 /* this interface is stopped */ | ||
139 | #define ISDN_NET_DM_MANUAL 0x40 /* this interface is on (manual) */ | ||
140 | #define ISDN_NET_DM_AUTO 0x80 /* this interface is autodial */ | ||
141 | #define ISDN_NET_DIALMODE(x) ((&(x))->flags & ISDN_NET_DIALMODE_MASK) | ||
142 | |||
143 | |||
144 | #endif /* _UAPI__ISDN_H__ */ | ||
diff --git a/include/uapi/linux/isdn_divertif.h b/include/uapi/linux/isdn_divertif.h deleted file mode 100644 index 0a17bb1bcb1b..000000000000 --- a/include/uapi/linux/isdn_divertif.h +++ /dev/null | |||
@@ -1,31 +0,0 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ | ||
2 | /* $Id: isdn_divertif.h,v 1.4.6.1 2001/09/23 22:25:05 kai Exp $ | ||
3 | * | ||
4 | * Header for the diversion supplementary interface for i4l. | ||
5 | * | ||
6 | * Author Werner Cornelius (werner@titro.de) | ||
7 | * Copyright by Werner Cornelius (werner@titro.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 | #ifndef _UAPI_LINUX_ISDN_DIVERTIF_H | ||
15 | #define _UAPI_LINUX_ISDN_DIVERTIF_H | ||
16 | |||
17 | /***********************************************************/ | ||
18 | /* magic value is also used to control version information */ | ||
19 | /***********************************************************/ | ||
20 | #define DIVERT_IF_MAGIC 0x25873401 | ||
21 | #define DIVERT_CMD_REG 0x00 /* register command */ | ||
22 | #define DIVERT_CMD_REL 0x01 /* release command */ | ||
23 | #define DIVERT_NO_ERR 0x00 /* return value no error */ | ||
24 | #define DIVERT_CMD_ERR 0x01 /* invalid cmd */ | ||
25 | #define DIVERT_VER_ERR 0x02 /* magic/version invalid */ | ||
26 | #define DIVERT_REG_ERR 0x03 /* module already registered */ | ||
27 | #define DIVERT_REL_ERR 0x04 /* module not registered */ | ||
28 | #define DIVERT_REG_NAME isdn_register_divert | ||
29 | |||
30 | |||
31 | #endif /* _UAPI_LINUX_ISDN_DIVERTIF_H */ | ||
diff --git a/include/uapi/linux/isdn_ppp.h b/include/uapi/linux/isdn_ppp.h deleted file mode 100644 index 0bdc4efaacb2..000000000000 --- a/include/uapi/linux/isdn_ppp.h +++ /dev/null | |||
@@ -1,68 +0,0 @@ | |||
1 | /* SPDX-License-Identifier: GPL-1.0+ WITH Linux-syscall-note */ | ||
2 | /* Linux ISDN subsystem, sync PPP, interface to ipppd | ||
3 | * | ||
4 | * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de) | ||
5 | * Copyright 1995,96 Thinking Objects Software GmbH Wuerzburg | ||
6 | * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) | ||
7 | * Copyright 2000-2002 by Kai Germaschewski (kai@germaschewski.name) | ||
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 | #ifndef _UAPI_LINUX_ISDN_PPP_H | ||
15 | #define _UAPI_LINUX_ISDN_PPP_H | ||
16 | |||
17 | #define CALLTYPE_INCOMING 0x1 | ||
18 | #define CALLTYPE_OUTGOING 0x2 | ||
19 | #define CALLTYPE_CALLBACK 0x4 | ||
20 | |||
21 | #define IPPP_VERSION "2.2.0" | ||
22 | |||
23 | struct pppcallinfo | ||
24 | { | ||
25 | int calltype; | ||
26 | unsigned char local_num[64]; | ||
27 | unsigned char remote_num[64]; | ||
28 | int charge_units; | ||
29 | }; | ||
30 | |||
31 | #define PPPIOCGCALLINFO _IOWR('t',128,struct pppcallinfo) | ||
32 | #define PPPIOCBUNDLE _IOW('t',129,int) | ||
33 | #define PPPIOCGMPFLAGS _IOR('t',130,int) | ||
34 | #define PPPIOCSMPFLAGS _IOW('t',131,int) | ||
35 | #define PPPIOCSMPMTU _IOW('t',132,int) | ||
36 | #define PPPIOCSMPMRU _IOW('t',133,int) | ||
37 | #define PPPIOCGCOMPRESSORS _IOR('t',134,unsigned long [8]) | ||
38 | #define PPPIOCSCOMPRESSOR _IOW('t',135,int) | ||
39 | #define PPPIOCGIFNAME _IOR('t',136, char [IFNAMSIZ] ) | ||
40 | |||
41 | |||
42 | #define SC_MP_PROT 0x00000200 | ||
43 | #define SC_REJ_MP_PROT 0x00000400 | ||
44 | #define SC_OUT_SHORT_SEQ 0x00000800 | ||
45 | #define SC_IN_SHORT_SEQ 0x00004000 | ||
46 | |||
47 | #define SC_DECOMP_ON 0x01 | ||
48 | #define SC_COMP_ON 0x02 | ||
49 | #define SC_DECOMP_DISCARD 0x04 | ||
50 | #define SC_COMP_DISCARD 0x08 | ||
51 | #define SC_LINK_DECOMP_ON 0x10 | ||
52 | #define SC_LINK_COMP_ON 0x20 | ||
53 | #define SC_LINK_DECOMP_DISCARD 0x40 | ||
54 | #define SC_LINK_COMP_DISCARD 0x80 | ||
55 | |||
56 | #define ISDN_PPP_COMP_MAX_OPTIONS 16 | ||
57 | |||
58 | #define IPPP_COMP_FLAG_XMIT 0x1 | ||
59 | #define IPPP_COMP_FLAG_LINK 0x2 | ||
60 | |||
61 | struct isdn_ppp_comp_data { | ||
62 | int num; | ||
63 | unsigned char options[ISDN_PPP_COMP_MAX_OPTIONS]; | ||
64 | int optlen; | ||
65 | int flags; | ||
66 | }; | ||
67 | |||
68 | #endif /* _UAPI_LINUX_ISDN_PPP_H */ | ||
diff --git a/include/uapi/linux/isdnif.h b/include/uapi/linux/isdnif.h deleted file mode 100644 index 611a69196738..000000000000 --- a/include/uapi/linux/isdnif.h +++ /dev/null | |||
@@ -1,57 +0,0 @@ | |||
1 | /* SPDX-License-Identifier: GPL-1.0+ WITH Linux-syscall-note */ | ||
2 | /* $Id: isdnif.h,v 1.43.2.2 2004/01/12 23:08:35 keil Exp $ | ||
3 | * | ||
4 | * Linux ISDN subsystem | ||
5 | * Definition of the interface between the subsystem and its low-level drivers. | ||
6 | * | ||
7 | * Copyright 1994,95,96 by Fritz Elfert (fritz@isdn4linux.de) | ||
8 | * Copyright 1995,96 Thinking Objects Software GmbH Wuerzburg | ||
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 | #ifndef _UAPI__ISDNIF_H__ | ||
16 | #define _UAPI__ISDNIF_H__ | ||
17 | |||
18 | |||
19 | /* | ||
20 | * Values for general protocol-selection | ||
21 | */ | ||
22 | #define ISDN_PTYPE_UNKNOWN 0 /* Protocol undefined */ | ||
23 | #define ISDN_PTYPE_1TR6 1 /* german 1TR6-protocol */ | ||
24 | #define ISDN_PTYPE_EURO 2 /* EDSS1-protocol */ | ||
25 | #define ISDN_PTYPE_LEASED 3 /* for leased lines */ | ||
26 | #define ISDN_PTYPE_NI1 4 /* US NI-1 protocol */ | ||
27 | #define ISDN_PTYPE_MAX 7 /* Max. 8 Protocols */ | ||
28 | |||
29 | /* | ||
30 | * Values for Layer-2-protocol-selection | ||
31 | */ | ||
32 | #define ISDN_PROTO_L2_X75I 0 /* X75/LAPB with I-Frames */ | ||
33 | #define ISDN_PROTO_L2_X75UI 1 /* X75/LAPB with UI-Frames */ | ||
34 | #define ISDN_PROTO_L2_X75BUI 2 /* X75/LAPB with UI-Frames */ | ||
35 | #define ISDN_PROTO_L2_HDLC 3 /* HDLC */ | ||
36 | #define ISDN_PROTO_L2_TRANS 4 /* Transparent (Voice) */ | ||
37 | #define ISDN_PROTO_L2_X25DTE 5 /* X25/LAPB DTE mode */ | ||
38 | #define ISDN_PROTO_L2_X25DCE 6 /* X25/LAPB DCE mode */ | ||
39 | #define ISDN_PROTO_L2_V11096 7 /* V.110 bitrate adaption 9600 Baud */ | ||
40 | #define ISDN_PROTO_L2_V11019 8 /* V.110 bitrate adaption 19200 Baud */ | ||
41 | #define ISDN_PROTO_L2_V11038 9 /* V.110 bitrate adaption 38400 Baud */ | ||
42 | #define ISDN_PROTO_L2_MODEM 10 /* Analog Modem on Board */ | ||
43 | #define ISDN_PROTO_L2_FAX 11 /* Fax Group 2/3 */ | ||
44 | #define ISDN_PROTO_L2_HDLC_56K 12 /* HDLC 56k */ | ||
45 | #define ISDN_PROTO_L2_MAX 15 /* Max. 16 Protocols */ | ||
46 | |||
47 | /* | ||
48 | * Values for Layer-3-protocol-selection | ||
49 | */ | ||
50 | #define ISDN_PROTO_L3_TRANS 0 /* Transparent */ | ||
51 | #define ISDN_PROTO_L3_TRANSDSP 1 /* Transparent with DSP */ | ||
52 | #define ISDN_PROTO_L3_FCLASS2 2 /* Fax Group 2/3 CLASS 2 */ | ||
53 | #define ISDN_PROTO_L3_FCLASS1 3 /* Fax Group 2/3 CLASS 1 */ | ||
54 | #define ISDN_PROTO_L3_MAX 7 /* Max. 8 Protocols */ | ||
55 | |||
56 | |||
57 | #endif /* _UAPI__ISDNIF_H__ */ | ||
diff --git a/include/uapi/linux/wanrouter.h b/include/uapi/linux/wanrouter.h deleted file mode 100644 index 2f1216d00caa..000000000000 --- a/include/uapi/linux/wanrouter.h +++ /dev/null | |||
@@ -1,18 +0,0 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ | ||
2 | /* | ||
3 | * wanrouter.h Legacy declarations kept around until X25 is removed | ||
4 | */ | ||
5 | |||
6 | #ifndef _UAPI_ROUTER_H | ||
7 | #define _UAPI_ROUTER_H | ||
8 | |||
9 | /* 'state' defines */ | ||
10 | enum wan_states | ||
11 | { | ||
12 | WAN_UNCONFIGURED, /* link/channel is not configured */ | ||
13 | WAN_DISCONNECTED, /* link/channel is disconnected */ | ||
14 | WAN_CONNECTING, /* connection is in progress */ | ||
15 | WAN_CONNECTED /* link/channel is operational */ | ||
16 | }; | ||
17 | |||
18 | #endif /* _UAPI_ROUTER_H */ | ||