diff options
| author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
| commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
| tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/isdn/pcbit | |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/isdn/pcbit')
| -rw-r--r-- | drivers/isdn/pcbit/Kconfig | 14 | ||||
| -rw-r--r-- | drivers/isdn/pcbit/Makefile | 9 | ||||
| -rw-r--r-- | drivers/isdn/pcbit/callbacks.c | 367 | ||||
| -rw-r--r-- | drivers/isdn/pcbit/callbacks.h | 49 | ||||
| -rw-r--r-- | drivers/isdn/pcbit/capi.c | 663 | ||||
| -rw-r--r-- | drivers/isdn/pcbit/capi.h | 88 | ||||
| -rw-r--r-- | drivers/isdn/pcbit/drv.c | 1088 | ||||
| -rw-r--r-- | drivers/isdn/pcbit/edss1.c | 325 | ||||
| -rw-r--r-- | drivers/isdn/pcbit/edss1.h | 99 | ||||
| -rw-r--r-- | drivers/isdn/pcbit/layer2.c | 732 | ||||
| -rw-r--r-- | drivers/isdn/pcbit/layer2.h | 288 | ||||
| -rw-r--r-- | drivers/isdn/pcbit/module.c | 130 | ||||
| -rw-r--r-- | drivers/isdn/pcbit/pcbit.h | 169 |
13 files changed, 4021 insertions, 0 deletions
diff --git a/drivers/isdn/pcbit/Kconfig b/drivers/isdn/pcbit/Kconfig new file mode 100644 index 000000000000..f06997faef16 --- /dev/null +++ b/drivers/isdn/pcbit/Kconfig | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # | ||
| 2 | # Config.in for PCBIT ISDN driver | ||
| 3 | # | ||
| 4 | config ISDN_DRV_PCBIT | ||
| 5 | tristate "PCBIT-D support" | ||
| 6 | depends on ISDN_I4L && ISA && (BROKEN || !PPC) | ||
| 7 | help | ||
| 8 | This enables support for the PCBIT ISDN-card. This card is | ||
| 9 | manufactured in Portugal by Octal. For running this card, | ||
| 10 | additional firmware is necessary, which has to be downloaded into | ||
| 11 | the card using a utility which is distributed separately. See | ||
| 12 | <file:Documentation/isdn/README> and | ||
| 13 | <file:Documentation/isdn/README.pcbit> for more information. | ||
| 14 | |||
diff --git a/drivers/isdn/pcbit/Makefile b/drivers/isdn/pcbit/Makefile new file mode 100644 index 000000000000..2d026c3242e8 --- /dev/null +++ b/drivers/isdn/pcbit/Makefile | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # Makefile for the pcbit ISDN device driver | ||
| 2 | |||
| 3 | # Each configuration option enables a list of files. | ||
| 4 | |||
| 5 | obj-$(CONFIG_ISDN_DRV_PCBIT) += pcbit.o | ||
| 6 | |||
| 7 | # Multipart objects. | ||
| 8 | |||
| 9 | pcbit-y := module.o edss1.o drv.o layer2.o capi.o callbacks.o | ||
diff --git a/drivers/isdn/pcbit/callbacks.c b/drivers/isdn/pcbit/callbacks.c new file mode 100644 index 000000000000..692ec72d1ee8 --- /dev/null +++ b/drivers/isdn/pcbit/callbacks.c | |||
| @@ -0,0 +1,367 @@ | |||
| 1 | /* | ||
| 2 | * Callbacks for the FSM | ||
| 3 | * | ||
| 4 | * Copyright (C) 1996 Universidade de Lisboa | ||
| 5 | * | ||
| 6 | * Written by Pedro Roque Marques (roque@di.fc.ul.pt) | ||
| 7 | * | ||
| 8 | * This software may be used and distributed according to the terms of | ||
| 9 | * the GNU General Public License, incorporated herein by reference. | ||
| 10 | */ | ||
| 11 | |||
| 12 | /* | ||
| 13 | * Fix: 19981230 - Carlos Morgado <chbm@techie.com> | ||
| 14 | * Port of Nelson Escravana's <nelson.escravana@usa.net> fix to CalledPN | ||
| 15 | * NULL pointer dereference in cb_in_1 (originally fixed in 2.0) | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/sched.h> | ||
| 19 | #include <linux/string.h> | ||
| 20 | #include <linux/kernel.h> | ||
| 21 | |||
| 22 | #include <linux/types.h> | ||
| 23 | #include <linux/slab.h> | ||
| 24 | #include <linux/mm.h> | ||
| 25 | #include <linux/skbuff.h> | ||
| 26 | |||
| 27 | #include <asm/io.h> | ||
| 28 | |||
| 29 | #include <linux/isdnif.h> | ||
| 30 | |||
| 31 | #include "pcbit.h" | ||
| 32 | #include "layer2.h" | ||
| 33 | #include "edss1.h" | ||
| 34 | #include "callbacks.h" | ||
| 35 | #include "capi.h" | ||
| 36 | |||
| 37 | ushort last_ref_num = 1; | ||
| 38 | |||
| 39 | /* | ||
| 40 | * send_conn_req | ||
| 41 | * | ||
| 42 | */ | ||
| 43 | |||
| 44 | void cb_out_1(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 45 | struct callb_data *cbdata) | ||
| 46 | { | ||
| 47 | struct sk_buff *skb; | ||
| 48 | int len; | ||
| 49 | ushort refnum; | ||
| 50 | |||
| 51 | |||
| 52 | #ifdef DEBUG | ||
| 53 | printk(KERN_DEBUG "Called Party Number: %s\n", | ||
| 54 | cbdata->data.setup.CalledPN); | ||
| 55 | #endif | ||
| 56 | /* | ||
| 57 | * hdr - kmalloc in capi_conn_req | ||
| 58 | * - kfree when msg has been sent | ||
| 59 | */ | ||
| 60 | |||
| 61 | if ((len = capi_conn_req(cbdata->data.setup.CalledPN, &skb, | ||
| 62 | chan->proto)) < 0) | ||
| 63 | { | ||
| 64 | printk("capi_conn_req failed\n"); | ||
| 65 | return; | ||
| 66 | } | ||
| 67 | |||
| 68 | |||
| 69 | refnum = last_ref_num++ & 0x7fffU; | ||
| 70 | |||
| 71 | chan->callref = 0; | ||
| 72 | chan->layer2link = 0; | ||
| 73 | chan->snum = 0; | ||
| 74 | chan->s_refnum = refnum; | ||
| 75 | |||
| 76 | pcbit_l2_write(dev, MSG_CONN_REQ, refnum, skb, len); | ||
| 77 | } | ||
| 78 | |||
| 79 | /* | ||
| 80 | * rcv CONNECT | ||
| 81 | * will go into ACTIVE state | ||
| 82 | * send CONN_ACTIVE_RESP | ||
| 83 | * send Select protocol request | ||
| 84 | */ | ||
| 85 | |||
| 86 | void cb_out_2(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 87 | struct callb_data *data) | ||
| 88 | { | ||
| 89 | isdn_ctrl ictl; | ||
| 90 | struct sk_buff *skb; | ||
| 91 | int len; | ||
| 92 | ushort refnum; | ||
| 93 | |||
| 94 | if ((len=capi_conn_active_resp(chan, &skb)) < 0) | ||
| 95 | { | ||
| 96 | printk("capi_conn_active_req failed\n"); | ||
| 97 | return; | ||
| 98 | } | ||
| 99 | |||
| 100 | refnum = last_ref_num++ & 0x7fffU; | ||
| 101 | chan->s_refnum = refnum; | ||
| 102 | |||
| 103 | pcbit_l2_write(dev, MSG_CONN_ACTV_RESP, refnum, skb, len); | ||
| 104 | |||
| 105 | |||
| 106 | ictl.command = ISDN_STAT_DCONN; | ||
| 107 | ictl.driver=dev->id; | ||
| 108 | ictl.arg=chan->id; | ||
| 109 | dev->dev_if->statcallb(&ictl); | ||
| 110 | |||
| 111 | /* ACTIVE D-channel */ | ||
| 112 | |||
| 113 | /* Select protocol */ | ||
| 114 | |||
| 115 | if ((len=capi_select_proto_req(chan, &skb, 1 /*outgoing*/)) < 0) { | ||
| 116 | printk("capi_select_proto_req failed\n"); | ||
| 117 | return; | ||
| 118 | } | ||
| 119 | |||
| 120 | refnum = last_ref_num++ & 0x7fffU; | ||
| 121 | chan->s_refnum = refnum; | ||
| 122 | |||
| 123 | pcbit_l2_write(dev, MSG_SELP_REQ, refnum, skb, len); | ||
| 124 | } | ||
| 125 | |||
| 126 | |||
| 127 | /* | ||
| 128 | * Disconnect received (actually RELEASE COMPLETE) | ||
| 129 | * This means we were not able to establish connection with remote | ||
| 130 | * Inform the big boss above | ||
| 131 | */ | ||
| 132 | void cb_out_3(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 133 | struct callb_data *data) | ||
| 134 | { | ||
| 135 | isdn_ctrl ictl; | ||
| 136 | |||
| 137 | ictl.command = ISDN_STAT_DHUP; | ||
| 138 | ictl.driver=dev->id; | ||
| 139 | ictl.arg=chan->id; | ||
| 140 | dev->dev_if->statcallb(&ictl); | ||
| 141 | } | ||
| 142 | |||
| 143 | |||
| 144 | /* | ||
| 145 | * Incoming call received | ||
| 146 | * inform user | ||
| 147 | */ | ||
| 148 | |||
| 149 | void cb_in_1(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 150 | struct callb_data *cbdata) | ||
| 151 | { | ||
| 152 | isdn_ctrl ictl; | ||
| 153 | unsigned short refnum; | ||
| 154 | struct sk_buff *skb; | ||
| 155 | int len; | ||
| 156 | |||
| 157 | |||
| 158 | ictl.command = ISDN_STAT_ICALL; | ||
| 159 | ictl.driver=dev->id; | ||
| 160 | ictl.arg=chan->id; | ||
| 161 | |||
| 162 | /* | ||
| 163 | * ictl.num >= strlen() + strlen() + 5 | ||
| 164 | */ | ||
| 165 | |||
| 166 | if (cbdata->data.setup.CallingPN == NULL) { | ||
| 167 | printk(KERN_DEBUG "NULL CallingPN to phone; using 0\n"); | ||
| 168 | strcpy(ictl.parm.setup.phone, "0"); | ||
| 169 | } | ||
| 170 | else { | ||
| 171 | strcpy(ictl.parm.setup.phone, cbdata->data.setup.CallingPN); | ||
| 172 | } | ||
| 173 | if (cbdata->data.setup.CalledPN == NULL) { | ||
| 174 | printk(KERN_DEBUG "NULL CalledPN to eazmsn; using 0\n"); | ||
| 175 | strcpy(ictl.parm.setup.eazmsn, "0"); | ||
| 176 | } | ||
| 177 | else { | ||
| 178 | strcpy(ictl.parm.setup.eazmsn, cbdata->data.setup.CalledPN); | ||
| 179 | } | ||
| 180 | ictl.parm.setup.si1 = 7; | ||
| 181 | ictl.parm.setup.si2 = 0; | ||
| 182 | ictl.parm.setup.plan = 0; | ||
| 183 | ictl.parm.setup.screen = 0; | ||
| 184 | |||
| 185 | #ifdef DEBUG | ||
| 186 | printk(KERN_DEBUG "statstr: %s\n", ictl.num); | ||
| 187 | #endif | ||
| 188 | |||
| 189 | dev->dev_if->statcallb(&ictl); | ||
| 190 | |||
| 191 | |||
| 192 | if ((len=capi_conn_resp(chan, &skb)) < 0) { | ||
| 193 | printk(KERN_DEBUG "capi_conn_resp failed\n"); | ||
| 194 | return; | ||
| 195 | } | ||
| 196 | |||
| 197 | refnum = last_ref_num++ & 0x7fffU; | ||
| 198 | chan->s_refnum = refnum; | ||
| 199 | |||
| 200 | pcbit_l2_write(dev, MSG_CONN_RESP, refnum, skb, len); | ||
| 201 | } | ||
| 202 | |||
| 203 | /* | ||
| 204 | * user has replied | ||
| 205 | * open the channel | ||
| 206 | * send CONNECT message CONNECT_ACTIVE_REQ in CAPI | ||
| 207 | */ | ||
| 208 | |||
| 209 | void cb_in_2(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 210 | struct callb_data *data) | ||
| 211 | { | ||
| 212 | unsigned short refnum; | ||
| 213 | struct sk_buff *skb; | ||
| 214 | int len; | ||
| 215 | |||
| 216 | if ((len = capi_conn_active_req(chan, &skb)) < 0) { | ||
| 217 | printk(KERN_DEBUG "capi_conn_active_req failed\n"); | ||
| 218 | return; | ||
| 219 | } | ||
| 220 | |||
| 221 | |||
| 222 | refnum = last_ref_num++ & 0x7fffU; | ||
| 223 | chan->s_refnum = refnum; | ||
| 224 | |||
| 225 | printk(KERN_DEBUG "sending MSG_CONN_ACTV_REQ\n"); | ||
| 226 | pcbit_l2_write(dev, MSG_CONN_ACTV_REQ, refnum, skb, len); | ||
| 227 | } | ||
| 228 | |||
| 229 | /* | ||
| 230 | * CONN_ACK arrived | ||
| 231 | * start b-proto selection | ||
| 232 | * | ||
| 233 | */ | ||
| 234 | |||
| 235 | void cb_in_3(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 236 | struct callb_data *data) | ||
| 237 | { | ||
| 238 | unsigned short refnum; | ||
| 239 | struct sk_buff *skb; | ||
| 240 | int len; | ||
| 241 | |||
| 242 | if ((len = capi_select_proto_req(chan, &skb, 0 /*incoming*/)) < 0) | ||
| 243 | { | ||
| 244 | printk("capi_select_proto_req failed\n"); | ||
| 245 | return; | ||
| 246 | } | ||
| 247 | |||
| 248 | refnum = last_ref_num++ & 0x7fffU; | ||
| 249 | chan->s_refnum = refnum; | ||
| 250 | |||
| 251 | pcbit_l2_write(dev, MSG_SELP_REQ, refnum, skb, len); | ||
| 252 | |||
| 253 | } | ||
| 254 | |||
| 255 | |||
| 256 | /* | ||
| 257 | * Received disconnect ind on active state | ||
| 258 | * send disconnect resp | ||
| 259 | * send msg to user | ||
| 260 | */ | ||
| 261 | void cb_disc_1(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 262 | struct callb_data *data) | ||
| 263 | { | ||
| 264 | struct sk_buff *skb; | ||
| 265 | int len; | ||
| 266 | ushort refnum; | ||
| 267 | isdn_ctrl ictl; | ||
| 268 | |||
| 269 | if ((len = capi_disc_resp(chan, &skb)) < 0) { | ||
| 270 | printk("capi_disc_resp failed\n"); | ||
| 271 | return; | ||
| 272 | } | ||
| 273 | |||
| 274 | refnum = last_ref_num++ & 0x7fffU; | ||
| 275 | chan->s_refnum = refnum; | ||
| 276 | |||
| 277 | pcbit_l2_write(dev, MSG_DISC_RESP, refnum, skb, len); | ||
| 278 | |||
| 279 | ictl.command = ISDN_STAT_BHUP; | ||
| 280 | ictl.driver=dev->id; | ||
| 281 | ictl.arg=chan->id; | ||
| 282 | dev->dev_if->statcallb(&ictl); | ||
| 283 | } | ||
| 284 | |||
| 285 | |||
| 286 | /* | ||
| 287 | * User HANGUP on active/call proceeding state | ||
| 288 | * send disc.req | ||
| 289 | */ | ||
| 290 | void cb_disc_2(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 291 | struct callb_data *data) | ||
| 292 | { | ||
| 293 | struct sk_buff *skb; | ||
| 294 | int len; | ||
| 295 | ushort refnum; | ||
| 296 | |||
| 297 | if ((len = capi_disc_req(chan->callref, &skb, CAUSE_NORMAL)) < 0) | ||
| 298 | { | ||
| 299 | printk("capi_disc_req failed\n"); | ||
| 300 | return; | ||
| 301 | } | ||
| 302 | |||
| 303 | refnum = last_ref_num++ & 0x7fffU; | ||
| 304 | chan->s_refnum = refnum; | ||
| 305 | |||
| 306 | pcbit_l2_write(dev, MSG_DISC_REQ, refnum, skb, len); | ||
| 307 | } | ||
| 308 | |||
| 309 | /* | ||
| 310 | * Disc confirm received send BHUP | ||
| 311 | * Problem: when the HL driver sends the disc req itself | ||
| 312 | * LL receives BHUP | ||
| 313 | */ | ||
| 314 | void cb_disc_3(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 315 | struct callb_data *data) | ||
| 316 | { | ||
| 317 | isdn_ctrl ictl; | ||
| 318 | |||
| 319 | ictl.command = ISDN_STAT_BHUP; | ||
| 320 | ictl.driver=dev->id; | ||
| 321 | ictl.arg=chan->id; | ||
| 322 | dev->dev_if->statcallb(&ictl); | ||
| 323 | } | ||
| 324 | |||
| 325 | void cb_notdone(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 326 | struct callb_data *data) | ||
| 327 | { | ||
| 328 | } | ||
| 329 | |||
| 330 | /* | ||
| 331 | * send activate b-chan protocol | ||
| 332 | */ | ||
| 333 | void cb_selp_1(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 334 | struct callb_data *data) | ||
| 335 | { | ||
| 336 | struct sk_buff *skb; | ||
| 337 | int len; | ||
| 338 | ushort refnum; | ||
| 339 | |||
| 340 | if ((len = capi_activate_transp_req(chan, &skb)) < 0) | ||
| 341 | { | ||
| 342 | printk("capi_conn_activate_transp_req failed\n"); | ||
| 343 | return; | ||
| 344 | } | ||
| 345 | |||
| 346 | refnum = last_ref_num++ & 0x7fffU; | ||
| 347 | chan->s_refnum = refnum; | ||
| 348 | |||
| 349 | pcbit_l2_write(dev, MSG_ACT_TRANSP_REQ, refnum, skb, len); | ||
| 350 | } | ||
| 351 | |||
| 352 | /* | ||
| 353 | * Inform User that the B-channel is available | ||
| 354 | */ | ||
| 355 | void cb_open(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 356 | struct callb_data *data) | ||
| 357 | { | ||
| 358 | isdn_ctrl ictl; | ||
| 359 | |||
| 360 | ictl.command = ISDN_STAT_BCONN; | ||
| 361 | ictl.driver=dev->id; | ||
| 362 | ictl.arg=chan->id; | ||
| 363 | dev->dev_if->statcallb(&ictl); | ||
| 364 | } | ||
| 365 | |||
| 366 | |||
| 367 | |||
diff --git a/drivers/isdn/pcbit/callbacks.h b/drivers/isdn/pcbit/callbacks.h new file mode 100644 index 000000000000..f510dc56b57e --- /dev/null +++ b/drivers/isdn/pcbit/callbacks.h | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | /* | ||
| 2 | * Callbacks prototypes for FSM | ||
| 3 | * | ||
| 4 | * Copyright (C) 1996 Universidade de Lisboa | ||
| 5 | * | ||
| 6 | * Written by Pedro Roque Marques (roque@di.fc.ul.pt) | ||
| 7 | * | ||
| 8 | * This software may be used and distributed according to the terms of | ||
| 9 | * the GNU General Public License, incorporated herein by reference. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef CALLBACKS_H | ||
| 13 | #define CALLBACKS_H | ||
| 14 | |||
| 15 | |||
| 16 | extern void cb_out_1(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 17 | struct callb_data *data); | ||
| 18 | |||
| 19 | extern void cb_out_2(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 20 | struct callb_data *data); | ||
| 21 | |||
| 22 | extern void cb_out_3(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 23 | struct callb_data *data); | ||
| 24 | |||
| 25 | extern void cb_in_1(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 26 | struct callb_data *data); | ||
| 27 | extern void cb_in_2(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 28 | struct callb_data *data); | ||
| 29 | extern void cb_in_3(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 30 | struct callb_data *data); | ||
| 31 | |||
| 32 | extern void cb_disc_1(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 33 | struct callb_data *data); | ||
| 34 | extern void cb_disc_2(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 35 | struct callb_data *data); | ||
| 36 | extern void cb_disc_3(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 37 | struct callb_data *data); | ||
| 38 | |||
| 39 | extern void cb_notdone(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 40 | struct callb_data *data); | ||
| 41 | |||
| 42 | extern void cb_selp_1(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 43 | struct callb_data *data); | ||
| 44 | extern void cb_open(struct pcbit_dev * dev, struct pcbit_chan* chan, | ||
| 45 | struct callb_data *data); | ||
| 46 | |||
| 47 | #endif | ||
| 48 | |||
| 49 | |||
diff --git a/drivers/isdn/pcbit/capi.c b/drivers/isdn/pcbit/capi.c new file mode 100644 index 000000000000..29eb03a8c29d --- /dev/null +++ b/drivers/isdn/pcbit/capi.c | |||
| @@ -0,0 +1,663 @@ | |||
| 1 | /* | ||
| 2 | * CAPI encoder/decoder for | ||
| 3 | * Portugal Telecom CAPI 2.0 | ||
| 4 | * | ||
| 5 | * Copyright (C) 1996 Universidade de Lisboa | ||
| 6 | * | ||
| 7 | * Written by Pedro Roque Marques (roque@di.fc.ul.pt) | ||
| 8 | * | ||
| 9 | * This software may be used and distributed according to the terms of | ||
| 10 | * the GNU General Public License, incorporated herein by reference. | ||
| 11 | * | ||
| 12 | * Not compatible with the AVM Gmbh. CAPI 2.0 | ||
| 13 | * | ||
| 14 | */ | ||
| 15 | |||
| 16 | /* | ||
| 17 | * Documentation: | ||
| 18 | * - "Common ISDN API - Perfil Português - Versão 2.1", | ||
| 19 | * Telecom Portugal, Fev 1992. | ||
| 20 | * - "Common ISDN API - Especificação de protocolos para | ||
| 21 | * acesso aos canais B", Inesc, Jan 1994. | ||
| 22 | */ | ||
| 23 | |||
| 24 | /* | ||
| 25 | * TODO: better decoding of Information Elements | ||
| 26 | * for debug purposes mainly | ||
| 27 | * encode our number in CallerPN and ConnectedPN | ||
| 28 | */ | ||
| 29 | |||
| 30 | #include <linux/sched.h> | ||
| 31 | #include <linux/string.h> | ||
| 32 | #include <linux/kernel.h> | ||
| 33 | |||
| 34 | #include <linux/types.h> | ||
| 35 | #include <linux/slab.h> | ||
| 36 | #include <linux/mm.h> | ||
| 37 | |||
| 38 | #include <linux/skbuff.h> | ||
| 39 | |||
| 40 | #include <asm/io.h> | ||
| 41 | #include <asm/string.h> | ||
| 42 | |||
| 43 | #include <linux/isdnif.h> | ||
| 44 | |||
| 45 | #include "pcbit.h" | ||
| 46 | #include "edss1.h" | ||
| 47 | #include "capi.h" | ||
| 48 | |||
| 49 | |||
| 50 | /* | ||
| 51 | * Encoding of CAPI messages | ||
| 52 | * | ||
| 53 | */ | ||
| 54 | |||
| 55 | int capi_conn_req(const char * calledPN, struct sk_buff **skb, int proto) | ||
| 56 | { | ||
| 57 | ushort len; | ||
| 58 | |||
| 59 | /* | ||
| 60 | * length | ||
| 61 | * AppInfoMask - 2 | ||
| 62 | * BC0 - 3 | ||
| 63 | * BC1 - 1 | ||
| 64 | * Chan - 2 | ||
| 65 | * Keypad - 1 | ||
| 66 | * CPN - 1 | ||
| 67 | * CPSA - 1 | ||
| 68 | * CalledPN - 2 + strlen | ||
| 69 | * CalledPSA - 1 | ||
| 70 | * rest... - 4 | ||
| 71 | * ---------------- | ||
| 72 | * Total 18 + strlen | ||
| 73 | */ | ||
| 74 | |||
| 75 | len = 18 + strlen(calledPN); | ||
| 76 | |||
| 77 | if (proto == ISDN_PROTO_L2_TRANS) | ||
| 78 | len++; | ||
| 79 | |||
| 80 | if ((*skb = dev_alloc_skb(len)) == NULL) { | ||
| 81 | |||
| 82 | printk(KERN_WARNING "capi_conn_req: alloc_skb failed\n"); | ||
| 83 | return -1; | ||
| 84 | } | ||
| 85 | |||
| 86 | /* InfoElmMask */ | ||
| 87 | *((ushort*) skb_put(*skb, 2)) = AppInfoMask; | ||
| 88 | |||
| 89 | if (proto == ISDN_PROTO_L2_TRANS) | ||
| 90 | { | ||
| 91 | /* Bearer Capability - Mandatory*/ | ||
| 92 | *(skb_put(*skb, 1)) = 3; /* BC0.Length */ | ||
| 93 | *(skb_put(*skb, 1)) = 0x80; /* Speech */ | ||
| 94 | *(skb_put(*skb, 1)) = 0x10; /* Circuit Mode */ | ||
| 95 | *(skb_put(*skb, 1)) = 0x23; /* A-law */ | ||
| 96 | } | ||
| 97 | else | ||
| 98 | { | ||
| 99 | /* Bearer Capability - Mandatory*/ | ||
| 100 | *(skb_put(*skb, 1)) = 2; /* BC0.Length */ | ||
| 101 | *(skb_put(*skb, 1)) = 0x88; /* Digital Information */ | ||
| 102 | *(skb_put(*skb, 1)) = 0x90; /* BC0.Octect4 */ | ||
| 103 | } | ||
| 104 | |||
| 105 | /* Bearer Capability - Optional*/ | ||
| 106 | *(skb_put(*skb, 1)) = 0; /* BC1.Length = 0 */ | ||
| 107 | |||
| 108 | *(skb_put(*skb, 1)) = 1; /* ChannelID.Length = 1 */ | ||
| 109 | *(skb_put(*skb, 1)) = 0x83; /* Basic Interface - Any Channel */ | ||
| 110 | |||
| 111 | *(skb_put(*skb, 1)) = 0; /* Keypad.Length = 0 */ | ||
| 112 | |||
| 113 | |||
| 114 | *(skb_put(*skb, 1)) = 0; /* CallingPN.Length = 0 */ | ||
| 115 | *(skb_put(*skb, 1)) = 0; /* CallingPSA.Length = 0 */ | ||
| 116 | |||
| 117 | /* Called Party Number */ | ||
| 118 | *(skb_put(*skb, 1)) = strlen(calledPN) + 1; | ||
| 119 | *(skb_put(*skb, 1)) = 0x81; | ||
| 120 | memcpy(skb_put(*skb, strlen(calledPN)), calledPN, strlen(calledPN)); | ||
| 121 | |||
| 122 | /* '#' */ | ||
| 123 | |||
| 124 | *(skb_put(*skb, 1)) = 0; /* CalledPSA.Length = 0 */ | ||
| 125 | |||
| 126 | /* LLC.Length = 0; */ | ||
| 127 | /* HLC0.Length = 0; */ | ||
| 128 | /* HLC1.Length = 0; */ | ||
| 129 | /* UTUS.Length = 0; */ | ||
| 130 | memset(skb_put(*skb, 4), 0, 4); | ||
| 131 | |||
| 132 | return len; | ||
| 133 | } | ||
| 134 | |||
| 135 | int capi_conn_resp(struct pcbit_chan* chan, struct sk_buff **skb) | ||
| 136 | { | ||
| 137 | |||
| 138 | if ((*skb = dev_alloc_skb(5)) == NULL) { | ||
| 139 | |||
| 140 | printk(KERN_WARNING "capi_conn_resp: alloc_skb failed\n"); | ||
| 141 | return -1; | ||
| 142 | } | ||
| 143 | |||
| 144 | *((ushort*) skb_put(*skb, 2) ) = chan->callref; | ||
| 145 | *(skb_put(*skb, 1)) = 0x01; /* ACCEPT_CALL */ | ||
| 146 | *(skb_put(*skb, 1)) = 0; | ||
| 147 | *(skb_put(*skb, 1)) = 0; | ||
| 148 | |||
| 149 | return 5; | ||
| 150 | } | ||
| 151 | |||
| 152 | int capi_conn_active_req(struct pcbit_chan* chan, struct sk_buff **skb) | ||
| 153 | { | ||
| 154 | /* | ||
| 155 | * 8 bytes | ||
| 156 | */ | ||
| 157 | |||
| 158 | if ((*skb = dev_alloc_skb(8)) == NULL) { | ||
| 159 | |||
| 160 | printk(KERN_WARNING "capi_conn_active_req: alloc_skb failed\n"); | ||
| 161 | return -1; | ||
| 162 | } | ||
| 163 | |||
| 164 | *((ushort*) skb_put(*skb, 2) ) = chan->callref; | ||
| 165 | |||
| 166 | #ifdef DEBUG | ||
| 167 | printk(KERN_DEBUG "Call Reference: %04x\n", chan->callref); | ||
| 168 | #endif | ||
| 169 | |||
| 170 | *(skb_put(*skb, 1)) = 0; /* BC.Length = 0; */ | ||
| 171 | *(skb_put(*skb, 1)) = 0; /* ConnectedPN.Length = 0 */ | ||
| 172 | *(skb_put(*skb, 1)) = 0; /* PSA.Length */ | ||
| 173 | *(skb_put(*skb, 1)) = 0; /* LLC.Length = 0; */ | ||
| 174 | *(skb_put(*skb, 1)) = 0; /* HLC.Length = 0; */ | ||
| 175 | *(skb_put(*skb, 1)) = 0; /* UTUS.Length = 0; */ | ||
| 176 | |||
| 177 | return 8; | ||
| 178 | } | ||
| 179 | |||
| 180 | int capi_conn_active_resp(struct pcbit_chan* chan, struct sk_buff **skb) | ||
| 181 | { | ||
| 182 | /* | ||
| 183 | * 2 bytes | ||
| 184 | */ | ||
| 185 | |||
| 186 | if ((*skb = dev_alloc_skb(2)) == NULL) { | ||
| 187 | |||
| 188 | printk(KERN_WARNING "capi_conn_active_resp: alloc_skb failed\n"); | ||
| 189 | return -1; | ||
| 190 | } | ||
| 191 | |||
| 192 | *((ushort*) skb_put(*skb, 2) ) = chan->callref; | ||
| 193 | |||
| 194 | return 2; | ||
| 195 | } | ||
| 196 | |||
| 197 | |||
| 198 | int capi_select_proto_req(struct pcbit_chan *chan, struct sk_buff **skb, | ||
| 199 | int outgoing) | ||
| 200 | { | ||
| 201 | |||
| 202 | /* | ||
| 203 | * 18 bytes | ||
| 204 | */ | ||
| 205 | |||
| 206 | if ((*skb = dev_alloc_skb(18)) == NULL) { | ||
| 207 | |||
| 208 | printk(KERN_WARNING "capi_select_proto_req: alloc_skb failed\n"); | ||
| 209 | return -1; | ||
| 210 | } | ||
| 211 | |||
| 212 | *((ushort*) skb_put(*skb, 2) ) = chan->callref; | ||
| 213 | |||
| 214 | /* Layer2 protocol */ | ||
| 215 | |||
| 216 | switch (chan->proto) { | ||
| 217 | case ISDN_PROTO_L2_X75I: | ||
| 218 | *(skb_put(*skb, 1)) = 0x05; /* LAPB */ | ||
| 219 | break; | ||
| 220 | case ISDN_PROTO_L2_HDLC: | ||
| 221 | *(skb_put(*skb, 1)) = 0x02; | ||
| 222 | break; | ||
| 223 | case ISDN_PROTO_L2_TRANS: | ||
| 224 | /* | ||
| 225 | * Voice (a-law) | ||
| 226 | */ | ||
| 227 | *(skb_put(*skb, 1)) = 0x06; | ||
| 228 | break; | ||
| 229 | default: | ||
| 230 | #ifdef DEBUG | ||
| 231 | printk(KERN_DEBUG "Transparent\n"); | ||
| 232 | #endif | ||
| 233 | *(skb_put(*skb, 1)) = 0x03; | ||
| 234 | break; | ||
| 235 | } | ||
| 236 | |||
| 237 | *(skb_put(*skb, 1)) = (outgoing ? 0x02 : 0x42); /* Don't ask */ | ||
| 238 | *(skb_put(*skb, 1)) = 0x00; | ||
| 239 | |||
| 240 | *((ushort *) skb_put(*skb, 2)) = MRU; | ||
| 241 | |||
| 242 | |||
| 243 | *(skb_put(*skb, 1)) = 0x08; /* Modulo */ | ||
| 244 | *(skb_put(*skb, 1)) = 0x07; /* Max Window */ | ||
| 245 | |||
| 246 | *(skb_put(*skb, 1)) = 0x01; /* No Layer3 Protocol */ | ||
| 247 | |||
| 248 | /* | ||
| 249 | * 2 - layer3 MTU [10] | ||
| 250 | * - Modulo [12] | ||
| 251 | * - Window | ||
| 252 | * - layer1 proto [14] | ||
| 253 | * - bitrate | ||
| 254 | * - sub-channel [16] | ||
| 255 | * - layer1dataformat [17] | ||
| 256 | */ | ||
| 257 | |||
| 258 | memset(skb_put(*skb, 8), 0, 8); | ||
| 259 | |||
| 260 | return 18; | ||
| 261 | } | ||
| 262 | |||
| 263 | |||
| 264 | int capi_activate_transp_req(struct pcbit_chan *chan, struct sk_buff **skb) | ||
| 265 | { | ||
| 266 | |||
| 267 | if ((*skb = dev_alloc_skb(7)) == NULL) { | ||
| 268 | |||
| 269 | printk(KERN_WARNING "capi_activate_transp_req: alloc_skb failed\n"); | ||
| 270 | return -1; | ||
| 271 | } | ||
| 272 | |||
| 273 | *((ushort*) skb_put(*skb, 2) ) = chan->callref; | ||
| 274 | |||
| 275 | |||
| 276 | *(skb_put(*skb, 1)) = chan->layer2link; /* Layer2 id */ | ||
| 277 | *(skb_put(*skb, 1)) = 0x00; /* Transmit by default */ | ||
| 278 | |||
| 279 | *((ushort *) skb_put(*skb, 2)) = MRU; | ||
| 280 | |||
| 281 | *(skb_put(*skb, 1)) = 0x01; /* Enables reception*/ | ||
| 282 | |||
| 283 | return 7; | ||
| 284 | } | ||
| 285 | |||
| 286 | int capi_tdata_req(struct pcbit_chan* chan, struct sk_buff *skb) | ||
| 287 | { | ||
| 288 | ushort data_len; | ||
| 289 | |||
| 290 | |||
| 291 | /* | ||
| 292 | * callref - 2 | ||
| 293 | * layer2link - 1 | ||
| 294 | * wBlockLength - 2 | ||
| 295 | * data - 4 | ||
| 296 | * sernum - 1 | ||
| 297 | */ | ||
| 298 | |||
| 299 | data_len = skb->len; | ||
| 300 | |||
| 301 | if(skb_headroom(skb) < 10) | ||
| 302 | { | ||
| 303 | printk(KERN_CRIT "No headspace (%u) on headroom %p for capi header\n", skb_headroom(skb), skb); | ||
| 304 | } | ||
| 305 | else | ||
| 306 | { | ||
| 307 | skb_push(skb, 10); | ||
| 308 | } | ||
| 309 | |||
| 310 | *((u16 *) (skb->data)) = chan->callref; | ||
| 311 | skb->data[2] = chan->layer2link; | ||
| 312 | *((u16 *) (skb->data + 3)) = data_len; | ||
| 313 | |||
| 314 | chan->s_refnum = (chan->s_refnum + 1) % 8; | ||
| 315 | *((u32 *) (skb->data + 5)) = chan->s_refnum; | ||
| 316 | |||
| 317 | skb->data[9] = 0; /* HDLC frame number */ | ||
| 318 | |||
| 319 | return 10; | ||
| 320 | } | ||
| 321 | |||
| 322 | int capi_tdata_resp(struct pcbit_chan *chan, struct sk_buff ** skb) | ||
| 323 | |||
| 324 | { | ||
| 325 | if ((*skb = dev_alloc_skb(4)) == NULL) { | ||
| 326 | |||
| 327 | printk(KERN_WARNING "capi_tdata_resp: alloc_skb failed\n"); | ||
| 328 | return -1; | ||
| 329 | } | ||
| 330 | |||
| 331 | *((ushort*) skb_put(*skb, 2) ) = chan->callref; | ||
| 332 | |||
| 333 | *(skb_put(*skb, 1)) = chan->layer2link; | ||
| 334 | *(skb_put(*skb, 1)) = chan->r_refnum; | ||
| 335 | |||
| 336 | return (*skb)->len; | ||
| 337 | } | ||
| 338 | |||
| 339 | int capi_disc_req(ushort callref, struct sk_buff **skb, u_char cause) | ||
| 340 | { | ||
| 341 | |||
| 342 | if ((*skb = dev_alloc_skb(6)) == NULL) { | ||
| 343 | |||
| 344 | printk(KERN_WARNING "capi_disc_req: alloc_skb failed\n"); | ||
| 345 | return -1; | ||
| 346 | } | ||
| 347 | |||
| 348 | *((ushort*) skb_put(*skb, 2) ) = callref; | ||
| 349 | |||
| 350 | *(skb_put(*skb, 1)) = 2; /* Cause.Length = 2; */ | ||
| 351 | *(skb_put(*skb, 1)) = 0x80; | ||
| 352 | *(skb_put(*skb, 1)) = 0x80 | cause; | ||
| 353 | |||
| 354 | /* | ||
| 355 | * Change it: we should send 'Sic transit gloria Mundi' here ;-) | ||
| 356 | */ | ||
| 357 | |||
| 358 | *(skb_put(*skb, 1)) = 0; /* UTUS.Length = 0; */ | ||
| 359 | |||
| 360 | return 6; | ||
| 361 | } | ||
| 362 | |||
| 363 | int capi_disc_resp(struct pcbit_chan *chan, struct sk_buff **skb) | ||
| 364 | { | ||
| 365 | if ((*skb = dev_alloc_skb(2)) == NULL) { | ||
| 366 | |||
| 367 | printk(KERN_WARNING "capi_disc_resp: alloc_skb failed\n"); | ||
| 368 | return -1; | ||
| 369 | } | ||
| 370 | |||
| 371 | *((ushort*) skb_put(*skb, 2)) = chan->callref; | ||
| 372 | |||
| 373 | return 2; | ||
| 374 | } | ||
| 375 | |||
| 376 | |||
| 377 | /* | ||
| 378 | * Decoding of CAPI messages | ||
| 379 | * | ||
| 380 | */ | ||
| 381 | |||
| 382 | int capi_decode_conn_ind(struct pcbit_chan * chan, | ||
| 383 | struct sk_buff *skb, | ||
| 384 | struct callb_data *info) | ||
| 385 | { | ||
| 386 | int CIlen, len; | ||
| 387 | |||
| 388 | /* Call Reference [CAPI] */ | ||
| 389 | chan->callref = *((ushort*) skb->data); | ||
| 390 | skb_pull(skb, 2); | ||
| 391 | |||
| 392 | #ifdef DEBUG | ||
| 393 | printk(KERN_DEBUG "Call Reference: %04x\n", chan->callref); | ||
| 394 | #endif | ||
| 395 | |||
| 396 | /* Channel Identification */ | ||
| 397 | |||
| 398 | /* Expect | ||
| 399 | Len = 1 | ||
| 400 | Octect 3 = 0100 10CC - [ 7 Basic, 4 , 2-1 chan ] | ||
| 401 | */ | ||
| 402 | |||
| 403 | CIlen = skb->data[0]; | ||
| 404 | #ifdef DEBUG | ||
| 405 | if (CIlen == 1) { | ||
| 406 | |||
| 407 | if ( ((skb->data[1]) & 0xFC) == 0x48 ) | ||
| 408 | printk(KERN_DEBUG "decode_conn_ind: chan ok\n"); | ||
| 409 | printk(KERN_DEBUG "phyChan = %d\n", skb->data[1] & 0x03); | ||
| 410 | } | ||
| 411 | else | ||
| 412 | printk(KERN_DEBUG "conn_ind: CIlen = %d\n", CIlen); | ||
| 413 | #endif | ||
| 414 | skb_pull(skb, CIlen + 1); | ||
| 415 | |||
| 416 | /* Calling Party Number */ | ||
| 417 | /* An "additional service" as far as Portugal Telecom is concerned */ | ||
| 418 | |||
| 419 | len = skb->data[0]; | ||
| 420 | |||
| 421 | if (len > 0) { | ||
| 422 | int count = 1; | ||
| 423 | |||
| 424 | #ifdef DEBUG | ||
| 425 | printk(KERN_DEBUG "CPN: Octect 3 %02x\n", skb->data[1]); | ||
| 426 | #endif | ||
| 427 | if ((skb->data[1] & 0x80) == 0) | ||
| 428 | count = 2; | ||
| 429 | |||
| 430 | if (!(info->data.setup.CallingPN = kmalloc(len - count + 1, GFP_ATOMIC))) | ||
| 431 | return -1; | ||
| 432 | |||
| 433 | memcpy(info->data.setup.CallingPN, skb->data + count + 1, | ||
| 434 | len - count); | ||
| 435 | info->data.setup.CallingPN[len - count] = 0; | ||
| 436 | |||
| 437 | } | ||
| 438 | else { | ||
| 439 | info->data.setup.CallingPN = NULL; | ||
| 440 | printk(KERN_DEBUG "NULL CallingPN\n"); | ||
| 441 | } | ||
| 442 | |||
| 443 | skb_pull(skb, len + 1); | ||
| 444 | |||
| 445 | /* Calling Party Subaddress */ | ||
| 446 | skb_pull(skb, skb->data[0] + 1); | ||
| 447 | |||
| 448 | /* Called Party Number */ | ||
| 449 | |||
| 450 | len = skb->data[0]; | ||
| 451 | |||
| 452 | if (len > 0) { | ||
| 453 | int count = 1; | ||
| 454 | |||
| 455 | if ((skb->data[1] & 0x80) == 0) | ||
| 456 | count = 2; | ||
| 457 | |||
| 458 | if (!(info->data.setup.CalledPN = kmalloc(len - count + 1, GFP_ATOMIC))) | ||
| 459 | return -1; | ||
| 460 | |||
| 461 | memcpy(info->data.setup.CalledPN, skb->data + count + 1, | ||
| 462 | len - count); | ||
| 463 | info->data.setup.CalledPN[len - count] = 0; | ||
| 464 | |||
| 465 | } | ||
| 466 | else { | ||
| 467 | info->data.setup.CalledPN = NULL; | ||
| 468 | printk(KERN_DEBUG "NULL CalledPN\n"); | ||
| 469 | } | ||
| 470 | |||
| 471 | skb_pull(skb, len + 1); | ||
| 472 | |||
| 473 | /* Called Party Subaddress */ | ||
| 474 | skb_pull(skb, skb->data[0] + 1); | ||
| 475 | |||
| 476 | /* LLC */ | ||
| 477 | skb_pull(skb, skb->data[0] + 1); | ||
| 478 | |||
| 479 | /* HLC */ | ||
| 480 | skb_pull(skb, skb->data[0] + 1); | ||
| 481 | |||
| 482 | /* U2U */ | ||
| 483 | skb_pull(skb, skb->data[0] + 1); | ||
| 484 | |||
| 485 | return 0; | ||
| 486 | } | ||
| 487 | |||
| 488 | /* | ||
| 489 | * returns errcode | ||
| 490 | */ | ||
| 491 | |||
| 492 | int capi_decode_conn_conf(struct pcbit_chan * chan, struct sk_buff *skb, | ||
| 493 | int *complete) | ||
| 494 | { | ||
| 495 | int errcode; | ||
| 496 | |||
| 497 | chan->callref = *((ushort *) skb->data); /* Update CallReference */ | ||
| 498 | skb_pull(skb, 2); | ||
| 499 | |||
| 500 | errcode = *((ushort *) skb->data); /* read errcode */ | ||
| 501 | skb_pull(skb, 2); | ||
| 502 | |||
| 503 | *complete = *(skb->data); | ||
| 504 | skb_pull(skb, 1); | ||
| 505 | |||
| 506 | /* FIX ME */ | ||
| 507 | /* This is actually a firmware bug */ | ||
| 508 | if (!*complete) | ||
| 509 | { | ||
| 510 | printk(KERN_DEBUG "complete=%02x\n", *complete); | ||
| 511 | *complete = 1; | ||
| 512 | } | ||
| 513 | |||
| 514 | |||
| 515 | /* Optional Bearer Capability */ | ||
| 516 | skb_pull(skb, *(skb->data) + 1); | ||
| 517 | |||
| 518 | /* Channel Identification */ | ||
| 519 | skb_pull(skb, *(skb->data) + 1); | ||
| 520 | |||
| 521 | /* High Layer Compatibility follows */ | ||
| 522 | skb_pull(skb, *(skb->data) + 1); | ||
| 523 | |||
| 524 | return errcode; | ||
| 525 | } | ||
| 526 | |||
| 527 | int capi_decode_conn_actv_ind(struct pcbit_chan * chan, struct sk_buff *skb) | ||
| 528 | { | ||
| 529 | ushort len; | ||
| 530 | #ifdef DEBUG | ||
| 531 | char str[32]; | ||
| 532 | #endif | ||
| 533 | |||
| 534 | /* Yet Another Bearer Capability */ | ||
| 535 | skb_pull(skb, *(skb->data) + 1); | ||
| 536 | |||
| 537 | |||
| 538 | /* Connected Party Number */ | ||
| 539 | len=*(skb->data); | ||
| 540 | |||
| 541 | #ifdef DEBUG | ||
| 542 | if (len > 1 && len < 31) { | ||
| 543 | memcpy(str, skb->data + 2, len - 1); | ||
| 544 | str[len] = 0; | ||
| 545 | printk(KERN_DEBUG "Connected Party Number: %s\n", str); | ||
| 546 | } | ||
| 547 | else | ||
| 548 | printk(KERN_DEBUG "actv_ind CPN len = %d\n", len); | ||
| 549 | #endif | ||
| 550 | |||
| 551 | skb_pull(skb, len + 1); | ||
| 552 | |||
| 553 | /* Connected Subaddress */ | ||
| 554 | skb_pull(skb, *(skb->data) + 1); | ||
| 555 | |||
| 556 | /* Low Layer Capability */ | ||
| 557 | skb_pull(skb, *(skb->data) + 1); | ||
| 558 | |||
| 559 | /* High Layer Capability */ | ||
| 560 | skb_pull(skb, *(skb->data) + 1); | ||
| 561 | |||
| 562 | return 0; | ||
| 563 | } | ||
| 564 | |||
| 565 | int capi_decode_conn_actv_conf(struct pcbit_chan * chan, struct sk_buff *skb) | ||
| 566 | { | ||
| 567 | ushort errcode; | ||
| 568 | |||
| 569 | errcode = *((ushort*) skb->data); | ||
| 570 | skb_pull(skb, 2); | ||
| 571 | |||
| 572 | /* Channel Identification | ||
| 573 | skb_pull(skb, skb->data[0] + 1); | ||
| 574 | */ | ||
| 575 | return errcode; | ||
| 576 | } | ||
| 577 | |||
| 578 | |||
| 579 | int capi_decode_sel_proto_conf(struct pcbit_chan *chan, struct sk_buff *skb) | ||
| 580 | { | ||
| 581 | ushort errcode; | ||
| 582 | |||
| 583 | chan->layer2link = *(skb->data); | ||
| 584 | skb_pull(skb, 1); | ||
| 585 | |||
| 586 | errcode = *((ushort*) skb->data); | ||
| 587 | skb_pull(skb, 2); | ||
| 588 | |||
| 589 | return errcode; | ||
| 590 | } | ||
| 591 | |||
| 592 | int capi_decode_actv_trans_conf(struct pcbit_chan *chan, struct sk_buff *skb) | ||
| 593 | { | ||
| 594 | ushort errcode; | ||
| 595 | |||
| 596 | if (chan->layer2link != *(skb->data) ) | ||
| 597 | printk("capi_decode_actv_trans_conf: layer2link doesn't match\n"); | ||
| 598 | |||
| 599 | skb_pull(skb, 1); | ||
| 600 | |||
| 601 | errcode = *((ushort*) skb->data); | ||
| 602 | skb_pull(skb, 2); | ||
| 603 | |||
| 604 | return errcode; | ||
| 605 | } | ||
| 606 | |||
| 607 | int capi_decode_disc_ind(struct pcbit_chan *chan, struct sk_buff *skb) | ||
| 608 | { | ||
| 609 | ushort len; | ||
| 610 | #ifdef DEBUG | ||
| 611 | int i; | ||
| 612 | #endif | ||
| 613 | /* Cause */ | ||
| 614 | |||
| 615 | len = *(skb->data); | ||
| 616 | skb_pull(skb, 1); | ||
| 617 | |||
| 618 | #ifdef DEBUG | ||
| 619 | |||
| 620 | for (i=0; i<len; i++) | ||
| 621 | printk(KERN_DEBUG "Cause Octect %d: %02x\n", i+3, | ||
| 622 | *(skb->data + i)); | ||
| 623 | #endif | ||
| 624 | |||
| 625 | skb_pull(skb, len); | ||
| 626 | |||
| 627 | return 0; | ||
| 628 | } | ||
| 629 | |||
| 630 | int capi_decode_disc_conf(struct pcbit_chan *chan, struct sk_buff *skb) | ||
| 631 | { | ||
| 632 | ushort errcode; | ||
| 633 | |||
| 634 | errcode = *((ushort*) skb->data); | ||
| 635 | skb_pull(skb, 2); | ||
| 636 | |||
| 637 | return errcode; | ||
| 638 | } | ||
| 639 | |||
| 640 | #ifdef DEBUG | ||
| 641 | int capi_decode_debug_188(u_char *hdr, ushort hdrlen) | ||
| 642 | { | ||
| 643 | char str[64]; | ||
| 644 | int len; | ||
| 645 | |||
| 646 | len = hdr[0]; | ||
| 647 | |||
| 648 | if (len < 64 && len == hdrlen - 1) { | ||
| 649 | memcpy(str, hdr + 1, hdrlen - 1); | ||
| 650 | str[hdrlen - 1] = 0; | ||
| 651 | printk("%s\n", str); | ||
| 652 | } | ||
| 653 | else | ||
| 654 | printk("debug message incorrect\n"); | ||
| 655 | |||
| 656 | return 0; | ||
| 657 | } | ||
| 658 | #endif | ||
| 659 | |||
| 660 | |||
| 661 | |||
| 662 | |||
| 663 | |||
diff --git a/drivers/isdn/pcbit/capi.h b/drivers/isdn/pcbit/capi.h new file mode 100644 index 000000000000..18e6aa360a8f --- /dev/null +++ b/drivers/isdn/pcbit/capi.h | |||
| @@ -0,0 +1,88 @@ | |||
| 1 | /* | ||
| 2 | * CAPI encode/decode prototypes and defines | ||
| 3 | * | ||
| 4 | * Copyright (C) 1996 Universidade de Lisboa | ||
| 5 | * | ||
| 6 | * Written by Pedro Roque Marques (roque@di.fc.ul.pt) | ||
| 7 | * | ||
| 8 | * This software may be used and distributed according to the terms of | ||
| 9 | * the GNU General Public License, incorporated herein by reference. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef CAPI_H | ||
| 13 | #define CAPI_H | ||
| 14 | |||
| 15 | |||
| 16 | #define REQ_CAUSE 0x01 | ||
| 17 | #define REQ_DISPLAY 0x04 | ||
| 18 | #define REQ_USER_TO_USER 0x08 | ||
| 19 | |||
| 20 | #define AppInfoMask REQ_CAUSE|REQ_DISPLAY|REQ_USER_TO_USER | ||
| 21 | |||
| 22 | /* Connection Setup */ | ||
| 23 | extern int capi_conn_req(const char * calledPN, struct sk_buff **buf, | ||
| 24 | int proto); | ||
| 25 | extern int capi_decode_conn_conf(struct pcbit_chan * chan, struct sk_buff *skb, | ||
| 26 | int *complete); | ||
| 27 | |||
| 28 | extern int capi_decode_conn_ind(struct pcbit_chan * chan, struct sk_buff *skb, | ||
| 29 | struct callb_data *info); | ||
| 30 | extern int capi_conn_resp(struct pcbit_chan* chan, struct sk_buff **skb); | ||
| 31 | |||
| 32 | extern int capi_conn_active_req(struct pcbit_chan* chan, struct sk_buff **skb); | ||
| 33 | extern int capi_decode_conn_actv_conf(struct pcbit_chan * chan, | ||
| 34 | struct sk_buff *skb); | ||
| 35 | |||
| 36 | extern int capi_decode_conn_actv_ind(struct pcbit_chan * chan, | ||
| 37 | struct sk_buff *skb); | ||
| 38 | extern int capi_conn_active_resp(struct pcbit_chan* chan, | ||
| 39 | struct sk_buff **skb); | ||
| 40 | |||
| 41 | /* Data */ | ||
| 42 | extern int capi_select_proto_req(struct pcbit_chan *chan, struct sk_buff **skb, | ||
| 43 | int outgoing); | ||
| 44 | extern int capi_decode_sel_proto_conf(struct pcbit_chan *chan, | ||
| 45 | struct sk_buff *skb); | ||
| 46 | |||
| 47 | extern int capi_activate_transp_req(struct pcbit_chan *chan, | ||
| 48 | struct sk_buff **skb); | ||
| 49 | extern int capi_decode_actv_trans_conf(struct pcbit_chan *chan, | ||
| 50 | struct sk_buff *skb); | ||
| 51 | |||
| 52 | extern int capi_tdata_req(struct pcbit_chan* chan, struct sk_buff *skb); | ||
| 53 | extern int capi_tdata_resp(struct pcbit_chan *chan, struct sk_buff ** skb); | ||
| 54 | |||
| 55 | /* Connection Termination */ | ||
| 56 | extern int capi_disc_req(ushort callref, struct sk_buff **skb, u_char cause); | ||
| 57 | extern int capi_decode_disc_conf(struct pcbit_chan *chan, struct sk_buff *skb); | ||
| 58 | |||
| 59 | extern int capi_decode_disc_ind(struct pcbit_chan *chan, struct sk_buff *skb); | ||
| 60 | extern int capi_disc_resp(struct pcbit_chan *chan, struct sk_buff **skb); | ||
| 61 | |||
| 62 | #ifdef DEBUG | ||
| 63 | extern int capi_decode_debug_188(u_char *hdr, ushort hdrlen); | ||
| 64 | #endif | ||
| 65 | |||
| 66 | static inline struct pcbit_chan * | ||
| 67 | capi_channel(struct pcbit_dev *dev, struct sk_buff *skb) | ||
| 68 | { | ||
| 69 | ushort callref; | ||
| 70 | |||
| 71 | callref = *((ushort*) skb->data); | ||
| 72 | skb_pull(skb, 2); | ||
| 73 | |||
| 74 | if (dev->b1->callref == callref) | ||
| 75 | return dev->b1; | ||
| 76 | else if (dev->b2->callref == callref) | ||
| 77 | return dev->b2; | ||
| 78 | |||
| 79 | return NULL; | ||
| 80 | } | ||
| 81 | |||
| 82 | #endif | ||
| 83 | |||
| 84 | |||
| 85 | |||
| 86 | |||
| 87 | |||
| 88 | |||
diff --git a/drivers/isdn/pcbit/drv.c b/drivers/isdn/pcbit/drv.c new file mode 100644 index 000000000000..e98f9c48c184 --- /dev/null +++ b/drivers/isdn/pcbit/drv.c | |||
| @@ -0,0 +1,1088 @@ | |||
| 1 | /* | ||
| 2 | * PCBIT-D interface with isdn4linux | ||
| 3 | * | ||
| 4 | * Copyright (C) 1996 Universidade de Lisboa | ||
| 5 | * | ||
| 6 | * Written by Pedro Roque Marques (roque@di.fc.ul.pt) | ||
| 7 | * | ||
| 8 | * This software may be used and distributed according to the terms of | ||
| 9 | * the GNU General Public License, incorporated herein by reference. | ||
| 10 | */ | ||
| 11 | |||
| 12 | /* | ||
| 13 | * Fixes: | ||
| 14 | * | ||
| 15 | * Nuno Grilo <l38486@alfa.ist.utl.pt> | ||
| 16 | * fixed msn_list NULL pointer dereference. | ||
| 17 | * | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/module.h> | ||
| 21 | |||
| 22 | #include <linux/sched.h> | ||
| 23 | |||
| 24 | #include <linux/kernel.h> | ||
| 25 | |||
| 26 | #include <linux/types.h> | ||
| 27 | #include <linux/slab.h> | ||
| 28 | #include <linux/mm.h> | ||
| 29 | #include <linux/interrupt.h> | ||
| 30 | #include <linux/string.h> | ||
| 31 | #include <linux/skbuff.h> | ||
| 32 | |||
| 33 | #include <linux/isdnif.h> | ||
| 34 | #include <asm/string.h> | ||
| 35 | #include <asm/io.h> | ||
| 36 | #include <linux/ioport.h> | ||
| 37 | |||
| 38 | #include "pcbit.h" | ||
| 39 | #include "edss1.h" | ||
| 40 | #include "layer2.h" | ||
| 41 | #include "capi.h" | ||
| 42 | |||
| 43 | |||
| 44 | extern ushort last_ref_num; | ||
| 45 | |||
| 46 | static int pcbit_ioctl(isdn_ctrl* ctl); | ||
| 47 | |||
| 48 | static char* pcbit_devname[MAX_PCBIT_CARDS] = { | ||
| 49 | "pcbit0", | ||
| 50 | "pcbit1", | ||
| 51 | "pcbit2", | ||
| 52 | "pcbit3" | ||
| 53 | }; | ||
| 54 | |||
| 55 | /* | ||
| 56 | * prototypes | ||
| 57 | */ | ||
| 58 | |||
| 59 | int pcbit_command(isdn_ctrl* ctl); | ||
| 60 | int pcbit_stat(u_char __user * buf, int len, int, int); | ||
| 61 | int pcbit_xmit(int driver, int chan, int ack, struct sk_buff *skb); | ||
| 62 | int pcbit_writecmd(const u_char __user *, int, int, int); | ||
| 63 | |||
| 64 | static int set_protocol_running(struct pcbit_dev * dev); | ||
| 65 | |||
| 66 | static void pcbit_clear_msn(struct pcbit_dev *dev); | ||
| 67 | static void pcbit_set_msn(struct pcbit_dev *dev, char *list); | ||
| 68 | static int pcbit_check_msn(struct pcbit_dev *dev, char *msn); | ||
| 69 | |||
| 70 | |||
| 71 | extern void pcbit_deliver(void * data); | ||
| 72 | |||
| 73 | int pcbit_init_dev(int board, int mem_base, int irq) | ||
| 74 | { | ||
| 75 | struct pcbit_dev *dev; | ||
| 76 | isdn_if *dev_if; | ||
| 77 | |||
| 78 | if ((dev=kmalloc(sizeof(struct pcbit_dev), GFP_KERNEL)) == NULL) | ||
| 79 | { | ||
| 80 | printk("pcbit_init: couldn't malloc pcbit_dev struct\n"); | ||
| 81 | return -ENOMEM; | ||
| 82 | } | ||
| 83 | |||
| 84 | dev_pcbit[board] = dev; | ||
| 85 | memset(dev, 0, sizeof(struct pcbit_dev)); | ||
| 86 | init_waitqueue_head(&dev->set_running_wq); | ||
| 87 | spin_lock_init(&dev->lock); | ||
| 88 | |||
| 89 | if (mem_base >= 0xA0000 && mem_base <= 0xFFFFF ) { | ||
| 90 | dev->ph_mem = mem_base; | ||
| 91 | if (!request_mem_region(dev->ph_mem, 4096, "PCBIT mem")) { | ||
| 92 | printk(KERN_WARNING | ||
| 93 | "PCBIT: memory region %lx-%lx already in use\n", | ||
| 94 | dev->ph_mem, dev->ph_mem + 4096); | ||
| 95 | kfree(dev); | ||
| 96 | dev_pcbit[board] = NULL; | ||
| 97 | return -EACCES; | ||
| 98 | } | ||
| 99 | dev->sh_mem = ioremap(dev->ph_mem, 4096); | ||
| 100 | } | ||
| 101 | else | ||
| 102 | { | ||
| 103 | printk("memory address invalid"); | ||
| 104 | kfree(dev); | ||
| 105 | dev_pcbit[board] = NULL; | ||
| 106 | return -EACCES; | ||
| 107 | } | ||
| 108 | |||
| 109 | dev->b1 = kmalloc(sizeof(struct pcbit_chan), GFP_KERNEL); | ||
| 110 | if (!dev->b1) { | ||
| 111 | printk("pcbit_init: couldn't malloc pcbit_chan struct\n"); | ||
| 112 | iounmap(dev->sh_mem); | ||
| 113 | release_mem_region(dev->ph_mem, 4096); | ||
| 114 | kfree(dev); | ||
| 115 | return -ENOMEM; | ||
| 116 | } | ||
| 117 | |||
| 118 | dev->b2 = kmalloc(sizeof(struct pcbit_chan), GFP_KERNEL); | ||
| 119 | if (!dev->b2) { | ||
| 120 | printk("pcbit_init: couldn't malloc pcbit_chan struct\n"); | ||
| 121 | kfree(dev->b1); | ||
| 122 | iounmap(dev->sh_mem); | ||
| 123 | release_mem_region(dev->ph_mem, 4096); | ||
| 124 | kfree(dev); | ||
| 125 | return -ENOMEM; | ||
| 126 | } | ||
| 127 | |||
| 128 | memset(dev->b1, 0, sizeof(struct pcbit_chan)); | ||
| 129 | memset(dev->b2, 0, sizeof(struct pcbit_chan)); | ||
| 130 | dev->b2->id = 1; | ||
| 131 | |||
| 132 | INIT_WORK(&dev->qdelivery, pcbit_deliver, dev); | ||
| 133 | |||
| 134 | /* | ||
| 135 | * interrupts | ||
| 136 | */ | ||
| 137 | |||
| 138 | if (request_irq(irq, &pcbit_irq_handler, 0, pcbit_devname[board], dev) != 0) | ||
| 139 | { | ||
| 140 | kfree(dev->b1); | ||
| 141 | kfree(dev->b2); | ||
| 142 | iounmap(dev->sh_mem); | ||
| 143 | release_mem_region(dev->ph_mem, 4096); | ||
| 144 | kfree(dev); | ||
| 145 | dev_pcbit[board] = NULL; | ||
| 146 | return -EIO; | ||
| 147 | } | ||
| 148 | |||
| 149 | dev->irq = irq; | ||
| 150 | |||
| 151 | /* next frame to be received */ | ||
| 152 | dev->rcv_seq = 0; | ||
| 153 | dev->send_seq = 0; | ||
| 154 | dev->unack_seq = 0; | ||
| 155 | |||
| 156 | dev->hl_hdrlen = 16; | ||
| 157 | |||
| 158 | dev_if = kmalloc(sizeof(isdn_if), GFP_KERNEL); | ||
| 159 | |||
| 160 | if (!dev_if) { | ||
| 161 | free_irq(irq, dev); | ||
| 162 | kfree(dev->b1); | ||
| 163 | kfree(dev->b2); | ||
| 164 | iounmap(dev->sh_mem); | ||
| 165 | release_mem_region(dev->ph_mem, 4096); | ||
| 166 | kfree(dev); | ||
| 167 | dev_pcbit[board] = NULL; | ||
| 168 | return -EIO; | ||
| 169 | } | ||
| 170 | |||
| 171 | dev->dev_if = dev_if; | ||
| 172 | |||
| 173 | dev_if->owner = THIS_MODULE; | ||
| 174 | |||
| 175 | dev_if->channels = 2; | ||
| 176 | |||
| 177 | dev_if->features = (ISDN_FEATURE_P_EURO | ISDN_FEATURE_L3_TRANS | | ||
| 178 | ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L2_TRANS ); | ||
| 179 | |||
| 180 | dev_if->writebuf_skb = pcbit_xmit; | ||
| 181 | dev_if->hl_hdrlen = 16; | ||
| 182 | |||
| 183 | dev_if->maxbufsize = MAXBUFSIZE; | ||
| 184 | dev_if->command = pcbit_command; | ||
| 185 | |||
| 186 | dev_if->writecmd = pcbit_writecmd; | ||
| 187 | dev_if->readstat = pcbit_stat; | ||
| 188 | |||
| 189 | |||
| 190 | strcpy(dev_if->id, pcbit_devname[board]); | ||
| 191 | |||
| 192 | if (!register_isdn(dev_if)) { | ||
| 193 | free_irq(irq, dev); | ||
| 194 | kfree(dev->b1); | ||
| 195 | kfree(dev->b2); | ||
| 196 | iounmap(dev->sh_mem); | ||
| 197 | release_mem_region(dev->ph_mem, 4096); | ||
| 198 | kfree(dev); | ||
| 199 | dev_pcbit[board] = NULL; | ||
| 200 | return -EIO; | ||
| 201 | } | ||
| 202 | |||
| 203 | dev->id = dev_if->channels; | ||
| 204 | |||
| 205 | |||
| 206 | dev->l2_state = L2_DOWN; | ||
| 207 | dev->free = 511; | ||
| 208 | |||
| 209 | /* | ||
| 210 | * set_protocol_running(dev); | ||
| 211 | */ | ||
| 212 | |||
| 213 | return 0; | ||
| 214 | } | ||
| 215 | |||
| 216 | #ifdef MODULE | ||
| 217 | void pcbit_terminate(int board) | ||
| 218 | { | ||
| 219 | struct pcbit_dev * dev; | ||
| 220 | |||
| 221 | dev = dev_pcbit[board]; | ||
| 222 | |||
| 223 | if (dev) { | ||
| 224 | /* unregister_isdn(dev->dev_if); */ | ||
| 225 | free_irq(dev->irq, dev); | ||
| 226 | pcbit_clear_msn(dev); | ||
| 227 | kfree(dev->dev_if); | ||
| 228 | if (dev->b1->fsm_timer.function) | ||
| 229 | del_timer(&dev->b1->fsm_timer); | ||
| 230 | if (dev->b2->fsm_timer.function) | ||
| 231 | del_timer(&dev->b2->fsm_timer); | ||
| 232 | kfree(dev->b1); | ||
| 233 | kfree(dev->b2); | ||
| 234 | iounmap(dev->sh_mem); | ||
| 235 | release_mem_region(dev->ph_mem, 4096); | ||
| 236 | kfree(dev); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | #endif | ||
| 240 | |||
| 241 | int pcbit_command(isdn_ctrl* ctl) | ||
| 242 | { | ||
| 243 | struct pcbit_dev *dev; | ||
| 244 | struct pcbit_chan *chan; | ||
| 245 | struct callb_data info; | ||
| 246 | |||
| 247 | dev = finddev(ctl->driver); | ||
| 248 | |||
| 249 | if (!dev) | ||
| 250 | { | ||
| 251 | printk("pcbit_command: unknown device\n"); | ||
| 252 | return -1; | ||
| 253 | } | ||
| 254 | |||
| 255 | chan = (ctl->arg & 0x0F) ? dev->b2 : dev->b1; | ||
| 256 | |||
| 257 | |||
| 258 | switch(ctl->command) { | ||
| 259 | case ISDN_CMD_IOCTL: | ||
| 260 | return pcbit_ioctl(ctl); | ||
| 261 | break; | ||
| 262 | case ISDN_CMD_DIAL: | ||
| 263 | info.type = EV_USR_SETUP_REQ; | ||
| 264 | info.data.setup.CalledPN = (char *) &ctl->parm.setup.phone; | ||
| 265 | pcbit_fsm_event(dev, chan, EV_USR_SETUP_REQ, &info); | ||
| 266 | break; | ||
| 267 | case ISDN_CMD_ACCEPTD: | ||
| 268 | pcbit_fsm_event(dev, chan, EV_USR_SETUP_RESP, NULL); | ||
| 269 | break; | ||
| 270 | case ISDN_CMD_ACCEPTB: | ||
| 271 | printk("ISDN_CMD_ACCEPTB - not really needed\n"); | ||
| 272 | break; | ||
| 273 | case ISDN_CMD_HANGUP: | ||
| 274 | pcbit_fsm_event(dev, chan, EV_USR_RELEASE_REQ, NULL); | ||
| 275 | break; | ||
| 276 | case ISDN_CMD_SETL2: | ||
| 277 | chan->proto = (ctl->arg >> 8); | ||
| 278 | break; | ||
| 279 | case ISDN_CMD_CLREAZ: | ||
| 280 | pcbit_clear_msn(dev); | ||
| 281 | break; | ||
| 282 | case ISDN_CMD_SETEAZ: | ||
| 283 | pcbit_set_msn(dev, ctl->parm.num); | ||
| 284 | break; | ||
| 285 | case ISDN_CMD_SETL3: | ||
| 286 | if ((ctl->arg >> 8) != ISDN_PROTO_L3_TRANS) | ||
| 287 | printk(KERN_DEBUG "L3 protocol unknown\n"); | ||
| 288 | break; | ||
| 289 | default: | ||
| 290 | printk(KERN_DEBUG "pcbit_command: unknown command\n"); | ||
| 291 | break; | ||
| 292 | }; | ||
| 293 | |||
| 294 | return 0; | ||
| 295 | } | ||
| 296 | |||
| 297 | /* | ||
| 298 | * Another Hack :-( | ||
| 299 | * on some conditions the board stops sending TDATA_CONFs | ||
| 300 | * let's see if we can turn around the problem | ||
| 301 | */ | ||
| 302 | |||
| 303 | #ifdef BLOCK_TIMER | ||
| 304 | static void pcbit_block_timer(unsigned long data) | ||
| 305 | { | ||
| 306 | struct pcbit_chan *chan; | ||
| 307 | struct pcbit_dev * dev; | ||
| 308 | isdn_ctrl ictl; | ||
| 309 | |||
| 310 | chan = (struct pcbit_chan *) data; | ||
| 311 | |||
| 312 | dev = chan2dev(chan); | ||
| 313 | |||
| 314 | if (dev == NULL) { | ||
| 315 | printk(KERN_DEBUG "pcbit: chan2dev failed\n"); | ||
| 316 | return; | ||
| 317 | } | ||
| 318 | |||
| 319 | del_timer(&chan->block_timer); | ||
| 320 | chan->block_timer.function = NULL; | ||
| 321 | |||
| 322 | #ifdef DEBUG | ||
| 323 | printk(KERN_DEBUG "pcbit_block_timer\n"); | ||
| 324 | #endif | ||
| 325 | chan->queued = 0; | ||
| 326 | ictl.driver = dev->id; | ||
| 327 | ictl.command = ISDN_STAT_BSENT; | ||
| 328 | ictl.arg = chan->id; | ||
| 329 | dev->dev_if->statcallb(&ictl); | ||
| 330 | } | ||
| 331 | #endif | ||
| 332 | |||
| 333 | int pcbit_xmit(int driver, int chnum, int ack, struct sk_buff *skb) | ||
| 334 | { | ||
| 335 | ushort hdrlen; | ||
| 336 | int refnum, len; | ||
| 337 | struct pcbit_chan * chan; | ||
| 338 | struct pcbit_dev *dev; | ||
| 339 | |||
| 340 | dev = finddev(driver); | ||
| 341 | if (dev == NULL) | ||
| 342 | { | ||
| 343 | printk("finddev returned NULL"); | ||
| 344 | return -1; | ||
| 345 | } | ||
| 346 | |||
| 347 | chan = chnum ? dev->b2 : dev->b1; | ||
| 348 | |||
| 349 | |||
| 350 | if (chan->fsm_state != ST_ACTIVE) | ||
| 351 | return -1; | ||
| 352 | |||
| 353 | if (chan->queued >= MAX_QUEUED ) | ||
| 354 | { | ||
| 355 | #ifdef DEBUG_QUEUE | ||
| 356 | printk(KERN_DEBUG | ||
| 357 | "pcbit: %d packets already in queue - write fails\n", | ||
| 358 | chan->queued); | ||
| 359 | #endif | ||
| 360 | /* | ||
| 361 | * packet stays on the head of the device queue | ||
| 362 | * since dev_start_xmit will fail | ||
| 363 | * see net/core/dev.c | ||
| 364 | */ | ||
| 365 | #ifdef BLOCK_TIMER | ||
| 366 | if (chan->block_timer.function == NULL) { | ||
| 367 | init_timer(&chan->block_timer); | ||
| 368 | chan->block_timer.function = &pcbit_block_timer; | ||
| 369 | chan->block_timer.data = (long) chan; | ||
| 370 | chan->block_timer.expires = jiffies + 1 * HZ; | ||
| 371 | add_timer(&chan->block_timer); | ||
| 372 | } | ||
| 373 | #endif | ||
| 374 | return 0; | ||
| 375 | } | ||
| 376 | |||
| 377 | |||
| 378 | chan->queued++; | ||
| 379 | |||
| 380 | len = skb->len; | ||
| 381 | |||
| 382 | hdrlen = capi_tdata_req(chan, skb); | ||
| 383 | |||
| 384 | refnum = last_ref_num++ & 0x7fffU; | ||
| 385 | chan->s_refnum = refnum; | ||
| 386 | |||
| 387 | pcbit_l2_write(dev, MSG_TDATA_REQ, refnum, skb, hdrlen); | ||
| 388 | |||
| 389 | return len; | ||
| 390 | } | ||
| 391 | |||
| 392 | int pcbit_writecmd(const u_char __user *buf, int len, int driver, int channel) | ||
| 393 | { | ||
| 394 | struct pcbit_dev * dev; | ||
| 395 | int i, j; | ||
| 396 | const u_char * loadbuf; | ||
| 397 | u_char * ptr = NULL; | ||
| 398 | u_char *cbuf; | ||
| 399 | |||
| 400 | int errstat; | ||
| 401 | |||
| 402 | dev = finddev(driver); | ||
| 403 | |||
| 404 | if (!dev) | ||
| 405 | { | ||
| 406 | printk("pcbit_writecmd: couldn't find device"); | ||
| 407 | return -ENODEV; | ||
| 408 | } | ||
| 409 | |||
| 410 | switch(dev->l2_state) { | ||
| 411 | case L2_LWMODE: | ||
| 412 | /* check (size <= rdp_size); write buf into board */ | ||
| 413 | if (len < 0 || len > BANK4 + 1 || len > 1024) | ||
| 414 | { | ||
| 415 | printk("pcbit_writecmd: invalid length %d\n", len); | ||
| 416 | return -EINVAL; | ||
| 417 | } | ||
| 418 | |||
| 419 | cbuf = kmalloc(len, GFP_KERNEL); | ||
| 420 | if (!cbuf) | ||
| 421 | return -ENOMEM; | ||
| 422 | |||
| 423 | if (copy_from_user(cbuf, buf, len)) { | ||
| 424 | kfree(cbuf); | ||
| 425 | return -EFAULT; | ||
| 426 | } | ||
| 427 | memcpy_toio(dev->sh_mem, cbuf, len); | ||
| 428 | kfree(cbuf); | ||
| 429 | return len; | ||
| 430 | case L2_FWMODE: | ||
| 431 | /* this is the hard part */ | ||
| 432 | /* dumb board */ | ||
| 433 | /* get it into kernel space */ | ||
| 434 | if ((ptr = kmalloc(len, GFP_KERNEL))==NULL) | ||
| 435 | return -ENOMEM; | ||
| 436 | if (copy_from_user(ptr, buf, len)) { | ||
| 437 | kfree(ptr); | ||
| 438 | return -EFAULT; | ||
| 439 | } | ||
| 440 | loadbuf = ptr; | ||
| 441 | |||
| 442 | errstat = 0; | ||
| 443 | |||
| 444 | for (i=0; i < len; i++) | ||
| 445 | { | ||
| 446 | for(j=0; j < LOAD_RETRY; j++) | ||
| 447 | if (!(readb(dev->sh_mem + dev->loadptr))) | ||
| 448 | break; | ||
| 449 | |||
| 450 | if (j == LOAD_RETRY) | ||
| 451 | { | ||
| 452 | errstat = -ETIME; | ||
| 453 | printk("TIMEOUT i=%d\n", i); | ||
| 454 | break; | ||
| 455 | } | ||
| 456 | writeb(loadbuf[i], dev->sh_mem + dev->loadptr + 1); | ||
| 457 | writeb(0x01, dev->sh_mem + dev->loadptr); | ||
| 458 | |||
| 459 | dev->loadptr += 2; | ||
| 460 | if (dev->loadptr > LOAD_ZONE_END) | ||
| 461 | dev->loadptr = LOAD_ZONE_START; | ||
| 462 | } | ||
| 463 | kfree(ptr); | ||
| 464 | |||
| 465 | return errstat ? errstat : len; | ||
| 466 | default: | ||
| 467 | return -EBUSY; | ||
| 468 | } | ||
| 469 | } | ||
| 470 | |||
| 471 | /* | ||
| 472 | * demultiplexing of messages | ||
| 473 | * | ||
| 474 | */ | ||
| 475 | |||
| 476 | void pcbit_l3_receive(struct pcbit_dev * dev, ulong msg, | ||
| 477 | struct sk_buff * skb, | ||
| 478 | ushort hdr_len, ushort refnum) | ||
| 479 | { | ||
| 480 | struct pcbit_chan *chan; | ||
| 481 | struct sk_buff *skb2; | ||
| 482 | unsigned short len; | ||
| 483 | struct callb_data cbdata; | ||
| 484 | int complete, err; | ||
| 485 | isdn_ctrl ictl; | ||
| 486 | |||
| 487 | switch(msg) { | ||
| 488 | |||
| 489 | case MSG_TDATA_IND: | ||
| 490 | if (!(chan = capi_channel(dev, skb))) { | ||
| 491 | printk(KERN_WARNING | ||
| 492 | "CAPI header: unknown channel id\n"); | ||
| 493 | break; | ||
| 494 | } | ||
| 495 | chan->r_refnum = skb->data[7]; | ||
| 496 | skb_pull(skb, 8); | ||
| 497 | |||
| 498 | dev->dev_if->rcvcallb_skb(dev->id, chan->id, skb); | ||
| 499 | |||
| 500 | if (capi_tdata_resp(chan, &skb2) > 0) | ||
| 501 | pcbit_l2_write(dev, MSG_TDATA_RESP, refnum, | ||
| 502 | skb2, skb2->len); | ||
| 503 | return; | ||
| 504 | break; | ||
| 505 | case MSG_TDATA_CONF: | ||
| 506 | if (!(chan = capi_channel(dev, skb))) { | ||
| 507 | printk(KERN_WARNING | ||
| 508 | "CAPI header: unknown channel id\n"); | ||
| 509 | break; | ||
| 510 | } | ||
| 511 | |||
| 512 | #ifdef DEBUG | ||
| 513 | if ( (*((ushort *) (skb->data + 2) )) != 0) { | ||
| 514 | printk(KERN_DEBUG "TDATA_CONF error\n"); | ||
| 515 | } | ||
| 516 | #endif | ||
| 517 | #ifdef BLOCK_TIMER | ||
| 518 | if (chan->queued == MAX_QUEUED) { | ||
| 519 | del_timer(&chan->block_timer); | ||
| 520 | chan->block_timer.function = NULL; | ||
| 521 | } | ||
| 522 | |||
| 523 | #endif | ||
| 524 | chan->queued--; | ||
| 525 | |||
| 526 | ictl.driver = dev->id; | ||
| 527 | ictl.command = ISDN_STAT_BSENT; | ||
| 528 | ictl.arg = chan->id; | ||
| 529 | dev->dev_if->statcallb(&ictl); | ||
| 530 | break; | ||
| 531 | |||
| 532 | case MSG_CONN_IND: | ||
| 533 | /* | ||
| 534 | * channel: 1st not used will do | ||
| 535 | * if both are used we're in trouble | ||
| 536 | */ | ||
| 537 | |||
| 538 | if (!dev->b1->fsm_state) | ||
| 539 | chan = dev->b1; | ||
| 540 | else if (!dev->b2->fsm_state) | ||
| 541 | chan = dev->b2; | ||
| 542 | else { | ||
| 543 | printk(KERN_INFO | ||
| 544 | "Incoming connection: no channels available"); | ||
| 545 | |||
| 546 | if ((len = capi_disc_req(*(ushort*)(skb->data), &skb2, CAUSE_NOCHAN)) > 0) | ||
| 547 | pcbit_l2_write(dev, MSG_DISC_REQ, refnum, skb2, len); | ||
| 548 | break; | ||
| 549 | } | ||
| 550 | |||
| 551 | cbdata.data.setup.CalledPN = NULL; | ||
| 552 | cbdata.data.setup.CallingPN = NULL; | ||
| 553 | |||
| 554 | capi_decode_conn_ind(chan, skb, &cbdata); | ||
| 555 | cbdata.type = EV_NET_SETUP; | ||
| 556 | |||
| 557 | pcbit_fsm_event(dev, chan, EV_NET_SETUP, NULL); | ||
| 558 | |||
| 559 | if (pcbit_check_msn(dev, cbdata.data.setup.CallingPN)) | ||
| 560 | pcbit_fsm_event(dev, chan, EV_USR_PROCED_REQ, &cbdata); | ||
| 561 | else | ||
| 562 | pcbit_fsm_event(dev, chan, EV_USR_RELEASE_REQ, NULL); | ||
| 563 | |||
| 564 | if (cbdata.data.setup.CalledPN) | ||
| 565 | kfree(cbdata.data.setup.CalledPN); | ||
| 566 | if (cbdata.data.setup.CallingPN) | ||
| 567 | kfree(cbdata.data.setup.CallingPN); | ||
| 568 | break; | ||
| 569 | |||
| 570 | case MSG_CONN_CONF: | ||
| 571 | /* | ||
| 572 | * We should be able to find the channel by the message | ||
| 573 | * reference number. The current version of the firmware | ||
| 574 | * doesn't sent the ref number correctly. | ||
| 575 | */ | ||
| 576 | #ifdef DEBUG | ||
| 577 | printk(KERN_DEBUG "refnum=%04x b1=%04x b2=%04x\n", refnum, | ||
| 578 | dev->b1->s_refnum, | ||
| 579 | dev->b2->s_refnum); | ||
| 580 | #endif | ||
| 581 | /* We just try to find a channel in the right state */ | ||
| 582 | |||
| 583 | if (dev->b1->fsm_state == ST_CALL_INIT) | ||
| 584 | chan = dev->b1; | ||
| 585 | else { | ||
| 586 | if (dev->b2->s_refnum == ST_CALL_INIT) | ||
| 587 | chan = dev->b2; | ||
| 588 | else { | ||
| 589 | chan = NULL; | ||
| 590 | printk(KERN_WARNING "Connection Confirm - no channel in Call Init state\n"); | ||
| 591 | break; | ||
| 592 | } | ||
| 593 | } | ||
| 594 | if (capi_decode_conn_conf(chan, skb, &complete)) { | ||
| 595 | printk(KERN_DEBUG "conn_conf indicates error\n"); | ||
| 596 | pcbit_fsm_event(dev, chan, EV_ERROR, NULL); | ||
| 597 | } | ||
| 598 | else | ||
| 599 | if (complete) | ||
| 600 | pcbit_fsm_event(dev, chan, EV_NET_CALL_PROC, NULL); | ||
| 601 | else | ||
| 602 | pcbit_fsm_event(dev, chan, EV_NET_SETUP_ACK, NULL); | ||
| 603 | break; | ||
| 604 | case MSG_CONN_ACTV_IND: | ||
| 605 | |||
| 606 | if (!(chan = capi_channel(dev, skb))) { | ||
| 607 | printk(KERN_WARNING | ||
| 608 | "CAPI header: unknown channel id\n"); | ||
| 609 | break; | ||
| 610 | } | ||
| 611 | |||
| 612 | if (capi_decode_conn_actv_ind(chan, skb)) { | ||
| 613 | printk("error in capi_decode_conn_actv_ind\n"); | ||
| 614 | /* pcbit_fsm_event(dev, chan, EV_ERROR, NULL); */ | ||
| 615 | break; | ||
| 616 | } | ||
| 617 | chan->r_refnum = refnum; | ||
| 618 | pcbit_fsm_event(dev, chan, EV_NET_CONN, NULL); | ||
| 619 | break; | ||
| 620 | case MSG_CONN_ACTV_CONF: | ||
| 621 | |||
| 622 | if (!(chan = capi_channel(dev, skb))) { | ||
| 623 | printk(KERN_WARNING | ||
| 624 | "CAPI header: unknown channel id\n"); | ||
| 625 | break; | ||
| 626 | } | ||
| 627 | |||
| 628 | if (capi_decode_conn_actv_conf(chan, skb) == 0) | ||
| 629 | pcbit_fsm_event(dev, chan, EV_NET_CONN_ACK, NULL); | ||
| 630 | |||
| 631 | else | ||
| 632 | printk(KERN_DEBUG "decode_conn_actv_conf failed\n"); | ||
| 633 | break; | ||
| 634 | |||
| 635 | case MSG_SELP_CONF: | ||
| 636 | |||
| 637 | if (!(chan = capi_channel(dev, skb))) { | ||
| 638 | printk(KERN_WARNING | ||
| 639 | "CAPI header: unknown channel id\n"); | ||
| 640 | break; | ||
| 641 | } | ||
| 642 | |||
| 643 | if (!(err = capi_decode_sel_proto_conf(chan, skb))) | ||
| 644 | pcbit_fsm_event(dev, chan, EV_NET_SELP_RESP, NULL); | ||
| 645 | else { | ||
| 646 | /* Error */ | ||
| 647 | printk("error %d - capi_decode_sel_proto_conf\n", err); | ||
| 648 | } | ||
| 649 | break; | ||
| 650 | case MSG_ACT_TRANSP_CONF: | ||
| 651 | if (!(chan = capi_channel(dev, skb))) { | ||
| 652 | printk(KERN_WARNING | ||
| 653 | "CAPI header: unknown channel id\n"); | ||
| 654 | break; | ||
| 655 | } | ||
| 656 | |||
| 657 | if (!capi_decode_actv_trans_conf(chan, skb)) | ||
| 658 | pcbit_fsm_event(dev, chan, EV_NET_ACTV_RESP, NULL); | ||
| 659 | break; | ||
| 660 | |||
| 661 | case MSG_DISC_IND: | ||
| 662 | |||
| 663 | if (!(chan = capi_channel(dev, skb))) { | ||
| 664 | printk(KERN_WARNING | ||
| 665 | "CAPI header: unknown channel id\n"); | ||
| 666 | break; | ||
| 667 | } | ||
| 668 | |||
| 669 | if (!capi_decode_disc_ind(chan, skb)) | ||
| 670 | pcbit_fsm_event(dev, chan, EV_NET_DISC, NULL); | ||
| 671 | else | ||
| 672 | printk(KERN_WARNING "capi_decode_disc_ind - error\n"); | ||
| 673 | break; | ||
| 674 | case MSG_DISC_CONF: | ||
| 675 | if (!(chan = capi_channel(dev, skb))) { | ||
| 676 | printk(KERN_WARNING | ||
| 677 | "CAPI header: unknown channel id\n"); | ||
| 678 | break; | ||
| 679 | } | ||
| 680 | |||
| 681 | if (!capi_decode_disc_ind(chan, skb)) | ||
| 682 | pcbit_fsm_event(dev, chan, EV_NET_RELEASE, NULL); | ||
| 683 | else | ||
| 684 | printk(KERN_WARNING "capi_decode_disc_conf - error\n"); | ||
| 685 | break; | ||
| 686 | case MSG_INFO_IND: | ||
| 687 | #ifdef DEBUG | ||
| 688 | printk(KERN_DEBUG "received Info Indication - discarded\n"); | ||
| 689 | #endif | ||
| 690 | break; | ||
| 691 | #ifdef DEBUG | ||
| 692 | case MSG_DEBUG_188: | ||
| 693 | capi_decode_debug_188(skb->data, skb->len); | ||
| 694 | break; | ||
| 695 | |||
| 696 | default: | ||
| 697 | printk(KERN_DEBUG "pcbit_l3_receive: unknown message %08lx\n", | ||
| 698 | msg); | ||
| 699 | break; | ||
| 700 | #endif | ||
| 701 | } | ||
| 702 | |||
| 703 | kfree_skb(skb); | ||
| 704 | |||
| 705 | } | ||
| 706 | |||
| 707 | /* | ||
| 708 | * Single statbuf | ||
| 709 | * should be a statbuf per device | ||
| 710 | */ | ||
| 711 | |||
| 712 | static char statbuf[STATBUF_LEN]; | ||
| 713 | static int stat_st = 0; | ||
| 714 | static int stat_end = 0; | ||
| 715 | |||
| 716 | int pcbit_stat(u_char __user *buf, int len, int driver, int channel) | ||
| 717 | { | ||
| 718 | int stat_count; | ||
| 719 | stat_count = stat_end - stat_st; | ||
| 720 | |||
| 721 | if (stat_count < 0) | ||
| 722 | stat_count = STATBUF_LEN - stat_st + stat_end; | ||
| 723 | |||
| 724 | /* FIXME: should we sleep and wait for more cookies ? */ | ||
| 725 | if (len > stat_count) | ||
| 726 | len = stat_count; | ||
| 727 | |||
| 728 | if (stat_st < stat_end) | ||
| 729 | { | ||
| 730 | copy_to_user(buf, statbuf + stat_st, len); | ||
| 731 | stat_st += len; | ||
| 732 | } | ||
| 733 | else | ||
| 734 | { | ||
| 735 | if (len > STATBUF_LEN - stat_st) | ||
| 736 | { | ||
| 737 | copy_to_user(buf, statbuf + stat_st, | ||
| 738 | STATBUF_LEN - stat_st); | ||
| 739 | copy_to_user(buf, statbuf, | ||
| 740 | len - (STATBUF_LEN - stat_st)); | ||
| 741 | |||
| 742 | stat_st = len - (STATBUF_LEN - stat_st); | ||
| 743 | } | ||
| 744 | else | ||
| 745 | { | ||
| 746 | copy_to_user(buf, statbuf + stat_st, len); | ||
| 747 | |||
| 748 | stat_st += len; | ||
| 749 | |||
| 750 | if (stat_st == STATBUF_LEN) | ||
| 751 | stat_st = 0; | ||
| 752 | } | ||
| 753 | } | ||
| 754 | |||
| 755 | if (stat_st == stat_end) | ||
| 756 | stat_st = stat_end = 0; | ||
| 757 | |||
| 758 | return len; | ||
| 759 | } | ||
| 760 | |||
| 761 | static void pcbit_logstat(struct pcbit_dev *dev, char *str) | ||
| 762 | { | ||
| 763 | int i; | ||
| 764 | isdn_ctrl ictl; | ||
| 765 | |||
| 766 | for (i=stat_end; i<strlen(str); i++) | ||
| 767 | { | ||
| 768 | statbuf[i]=str[i]; | ||
| 769 | stat_end = (stat_end + 1) % STATBUF_LEN; | ||
| 770 | if (stat_end == stat_st) | ||
| 771 | stat_st = (stat_st + 1) % STATBUF_LEN; | ||
| 772 | } | ||
| 773 | |||
| 774 | ictl.command=ISDN_STAT_STAVAIL; | ||
| 775 | ictl.driver=dev->id; | ||
| 776 | ictl.arg=strlen(str); | ||
| 777 | dev->dev_if->statcallb(&ictl); | ||
| 778 | } | ||
| 779 | |||
| 780 | extern char * isdn_state_table[]; | ||
| 781 | extern char * strisdnevent(unsigned short); | ||
| 782 | |||
| 783 | |||
| 784 | void pcbit_state_change(struct pcbit_dev * dev, struct pcbit_chan * chan, | ||
| 785 | unsigned short i, unsigned short ev, unsigned short f) | ||
| 786 | { | ||
| 787 | char buf[256]; | ||
| 788 | |||
| 789 | sprintf(buf, "change on device: %d channel:%d\n%s -> %s -> %s\n", | ||
| 790 | dev->id, chan->id, | ||
| 791 | isdn_state_table[i], strisdnevent(ev), isdn_state_table[f] | ||
| 792 | ); | ||
| 793 | |||
| 794 | #ifdef DEBUG | ||
| 795 | printk("%s", buf); | ||
| 796 | #endif | ||
| 797 | |||
| 798 | pcbit_logstat(dev, buf); | ||
| 799 | } | ||
| 800 | |||
| 801 | static void set_running_timeout(unsigned long ptr) | ||
| 802 | { | ||
| 803 | struct pcbit_dev * dev; | ||
| 804 | |||
| 805 | #ifdef DEBUG | ||
| 806 | printk(KERN_DEBUG "set_running_timeout\n"); | ||
| 807 | #endif | ||
| 808 | dev = (struct pcbit_dev *) ptr; | ||
| 809 | |||
| 810 | wake_up_interruptible(&dev->set_running_wq); | ||
| 811 | } | ||
| 812 | |||
| 813 | static int set_protocol_running(struct pcbit_dev * dev) | ||
| 814 | { | ||
| 815 | isdn_ctrl ctl; | ||
| 816 | |||
| 817 | init_timer(&dev->set_running_timer); | ||
| 818 | |||
| 819 | dev->set_running_timer.function = &set_running_timeout; | ||
| 820 | dev->set_running_timer.data = (ulong) dev; | ||
| 821 | dev->set_running_timer.expires = jiffies + SET_RUN_TIMEOUT; | ||
| 822 | |||
| 823 | /* kick it */ | ||
| 824 | |||
| 825 | dev->l2_state = L2_STARTING; | ||
| 826 | |||
| 827 | writeb((0x80U | ((dev->rcv_seq & 0x07) << 3) | (dev->send_seq & 0x07)), | ||
| 828 | dev->sh_mem + BANK4); | ||
| 829 | |||
| 830 | add_timer(&dev->set_running_timer); | ||
| 831 | |||
| 832 | interruptible_sleep_on(&dev->set_running_wq); | ||
| 833 | |||
| 834 | del_timer(&dev->set_running_timer); | ||
| 835 | |||
| 836 | if (dev->l2_state == L2_RUNNING) | ||
| 837 | { | ||
| 838 | printk(KERN_DEBUG "pcbit: running\n"); | ||
| 839 | |||
| 840 | dev->unack_seq = dev->send_seq; | ||
| 841 | |||
| 842 | dev->writeptr = dev->sh_mem; | ||
| 843 | dev->readptr = dev->sh_mem + BANK2; | ||
| 844 | |||
| 845 | /* tell the good news to the upper layer */ | ||
| 846 | ctl.driver = dev->id; | ||
| 847 | ctl.command = ISDN_STAT_RUN; | ||
| 848 | |||
| 849 | dev->dev_if->statcallb(&ctl); | ||
| 850 | } | ||
| 851 | else | ||
| 852 | { | ||
| 853 | printk(KERN_DEBUG "pcbit: initialization failed\n"); | ||
| 854 | printk(KERN_DEBUG "pcbit: firmware not loaded\n"); | ||
| 855 | |||
| 856 | dev->l2_state = L2_DOWN; | ||
| 857 | |||
| 858 | #ifdef DEBUG | ||
| 859 | printk(KERN_DEBUG "Bank3 = %02x\n", | ||
| 860 | readb(dev->sh_mem + BANK3)); | ||
| 861 | #endif | ||
| 862 | writeb(0x40, dev->sh_mem + BANK4); | ||
| 863 | |||
| 864 | /* warn the upper layer */ | ||
| 865 | ctl.driver = dev->id; | ||
| 866 | ctl.command = ISDN_STAT_STOP; | ||
| 867 | |||
| 868 | dev->dev_if->statcallb(&ctl); | ||
| 869 | |||
| 870 | return -EL2HLT; /* Level 2 halted */ | ||
| 871 | } | ||
| 872 | |||
| 873 | return 0; | ||
| 874 | } | ||
| 875 | |||
| 876 | static int pcbit_ioctl(isdn_ctrl* ctl) | ||
| 877 | { | ||
| 878 | struct pcbit_dev * dev; | ||
| 879 | struct pcbit_ioctl *cmd; | ||
| 880 | |||
| 881 | dev = finddev(ctl->driver); | ||
| 882 | |||
| 883 | if (!dev) | ||
| 884 | { | ||
| 885 | printk(KERN_DEBUG "pcbit_ioctl: unknown device\n"); | ||
| 886 | return -ENODEV; | ||
| 887 | } | ||
| 888 | |||
| 889 | cmd = (struct pcbit_ioctl *) ctl->parm.num; | ||
| 890 | |||
| 891 | switch(ctl->arg) { | ||
| 892 | case PCBIT_IOCTL_GETSTAT: | ||
| 893 | cmd->info.l2_status = dev->l2_state; | ||
| 894 | break; | ||
| 895 | |||
| 896 | case PCBIT_IOCTL_STRLOAD: | ||
| 897 | if (dev->l2_state == L2_RUNNING) | ||
| 898 | return -EBUSY; | ||
| 899 | |||
| 900 | dev->unack_seq = dev->send_seq = dev->rcv_seq = 0; | ||
| 901 | |||
| 902 | dev->writeptr = dev->sh_mem; | ||
| 903 | dev->readptr = dev->sh_mem + BANK2; | ||
| 904 | |||
| 905 | dev->l2_state = L2_LOADING; | ||
| 906 | break; | ||
| 907 | |||
| 908 | case PCBIT_IOCTL_LWMODE: | ||
| 909 | if (dev->l2_state != L2_LOADING) | ||
| 910 | return -EINVAL; | ||
| 911 | |||
| 912 | dev->l2_state = L2_LWMODE; | ||
| 913 | break; | ||
| 914 | |||
| 915 | case PCBIT_IOCTL_FWMODE: | ||
| 916 | if (dev->l2_state == L2_RUNNING) | ||
| 917 | return -EBUSY; | ||
| 918 | dev->loadptr = LOAD_ZONE_START; | ||
| 919 | dev->l2_state = L2_FWMODE; | ||
| 920 | |||
| 921 | break; | ||
| 922 | case PCBIT_IOCTL_ENDLOAD: | ||
| 923 | if (dev->l2_state == L2_RUNNING) | ||
| 924 | return -EBUSY; | ||
| 925 | dev->l2_state = L2_DOWN; | ||
| 926 | break; | ||
| 927 | |||
| 928 | case PCBIT_IOCTL_SETBYTE: | ||
| 929 | if (dev->l2_state == L2_RUNNING) | ||
| 930 | return -EBUSY; | ||
| 931 | |||
| 932 | /* check addr */ | ||
| 933 | if (cmd->info.rdp_byte.addr > BANK4) | ||
| 934 | return -EFAULT; | ||
| 935 | |||
| 936 | writeb(cmd->info.rdp_byte.value, dev->sh_mem + cmd->info.rdp_byte.addr); | ||
| 937 | break; | ||
| 938 | case PCBIT_IOCTL_GETBYTE: | ||
| 939 | if (dev->l2_state == L2_RUNNING) | ||
| 940 | return -EBUSY; | ||
| 941 | |||
| 942 | /* check addr */ | ||
| 943 | |||
| 944 | if (cmd->info.rdp_byte.addr > BANK4) | ||
| 945 | { | ||
| 946 | printk("getbyte: invalid addr %04x\n", cmd->info.rdp_byte.addr); | ||
| 947 | return -EFAULT; | ||
| 948 | } | ||
| 949 | |||
| 950 | cmd->info.rdp_byte.value = readb(dev->sh_mem + cmd->info.rdp_byte.addr); | ||
| 951 | break; | ||
| 952 | case PCBIT_IOCTL_RUNNING: | ||
| 953 | if (dev->l2_state == L2_RUNNING) | ||
| 954 | return -EBUSY; | ||
| 955 | return set_protocol_running(dev); | ||
| 956 | break; | ||
| 957 | case PCBIT_IOCTL_WATCH188: | ||
| 958 | if (dev->l2_state != L2_LOADING) | ||
| 959 | return -EINVAL; | ||
| 960 | pcbit_l2_write(dev, MSG_WATCH188, 0x0001, NULL, 0); | ||
| 961 | break; | ||
| 962 | case PCBIT_IOCTL_PING188: | ||
| 963 | if (dev->l2_state != L2_LOADING) | ||
| 964 | return -EINVAL; | ||
| 965 | pcbit_l2_write(dev, MSG_PING188_REQ, 0x0001, NULL, 0); | ||
| 966 | break; | ||
| 967 | case PCBIT_IOCTL_APION: | ||
| 968 | if (dev->l2_state != L2_LOADING) | ||
| 969 | return -EINVAL; | ||
| 970 | pcbit_l2_write(dev, MSG_API_ON, 0x0001, NULL, 0); | ||
| 971 | break; | ||
| 972 | case PCBIT_IOCTL_STOP: | ||
| 973 | dev->l2_state = L2_DOWN; | ||
| 974 | writeb(0x40, dev->sh_mem + BANK4); | ||
| 975 | dev->rcv_seq = 0; | ||
| 976 | dev->send_seq = 0; | ||
| 977 | dev->unack_seq = 0; | ||
| 978 | break; | ||
| 979 | default: | ||
| 980 | printk("error: unknown ioctl\n"); | ||
| 981 | break; | ||
| 982 | }; | ||
| 983 | return 0; | ||
| 984 | } | ||
| 985 | |||
| 986 | /* | ||
| 987 | * MSN list handling | ||
| 988 | * | ||
| 989 | * if null reject all calls | ||
| 990 | * if first entry has null MSN accept all calls | ||
| 991 | */ | ||
| 992 | |||
| 993 | static void pcbit_clear_msn(struct pcbit_dev *dev) | ||
| 994 | { | ||
| 995 | struct msn_entry *ptr, *back; | ||
| 996 | |||
| 997 | for (ptr=dev->msn_list; ptr; ) | ||
| 998 | { | ||
| 999 | back = ptr->next; | ||
| 1000 | kfree(ptr); | ||
| 1001 | ptr = back; | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | dev->msn_list = NULL; | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | static void pcbit_set_msn(struct pcbit_dev *dev, char *list) | ||
| 1008 | { | ||
| 1009 | struct msn_entry *ptr; | ||
| 1010 | struct msn_entry *back = NULL; | ||
| 1011 | char *cp, *sp; | ||
| 1012 | int len; | ||
| 1013 | |||
| 1014 | if (strlen(list) == 0) { | ||
| 1015 | ptr = kmalloc(sizeof(struct msn_entry), GFP_ATOMIC); | ||
| 1016 | if (!ptr) { | ||
| 1017 | printk(KERN_WARNING "kmalloc failed\n"); | ||
| 1018 | return; | ||
| 1019 | } | ||
| 1020 | |||
| 1021 | ptr->msn = NULL; | ||
| 1022 | |||
| 1023 | ptr->next = dev->msn_list; | ||
| 1024 | dev->msn_list = ptr; | ||
| 1025 | |||
| 1026 | return; | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | if (dev->msn_list) | ||
| 1030 | for (back=dev->msn_list; back->next; back=back->next); | ||
| 1031 | |||
| 1032 | sp = list; | ||
| 1033 | |||
| 1034 | do { | ||
| 1035 | cp=strchr(sp, ','); | ||
| 1036 | if (cp) | ||
| 1037 | len = cp - sp; | ||
| 1038 | else | ||
| 1039 | len = strlen(sp); | ||
| 1040 | |||
| 1041 | ptr = kmalloc(sizeof(struct msn_entry), GFP_ATOMIC); | ||
| 1042 | |||
| 1043 | if (!ptr) { | ||
| 1044 | printk(KERN_WARNING "kmalloc failed\n"); | ||
| 1045 | return; | ||
| 1046 | } | ||
| 1047 | ptr->next = NULL; | ||
| 1048 | |||
| 1049 | ptr->msn = kmalloc(len, GFP_ATOMIC); | ||
| 1050 | if (!ptr->msn) { | ||
| 1051 | printk(KERN_WARNING "kmalloc failed\n"); | ||
| 1052 | kfree(ptr); | ||
| 1053 | return; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | memcpy(ptr->msn, sp, len - 1); | ||
| 1057 | ptr->msn[len] = 0; | ||
| 1058 | |||
| 1059 | #ifdef DEBUG | ||
| 1060 | printk(KERN_DEBUG "msn: %s\n", ptr->msn); | ||
| 1061 | #endif | ||
| 1062 | if (dev->msn_list == NULL) | ||
| 1063 | dev->msn_list = ptr; | ||
| 1064 | else | ||
| 1065 | back->next = ptr; | ||
| 1066 | back = ptr; | ||
| 1067 | sp += len; | ||
| 1068 | } while(cp); | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | /* | ||
| 1072 | * check if we do signal or reject an incoming call | ||
| 1073 | */ | ||
| 1074 | static int pcbit_check_msn(struct pcbit_dev *dev, char *msn) | ||
| 1075 | { | ||
| 1076 | struct msn_entry *ptr; | ||
| 1077 | |||
| 1078 | for (ptr=dev->msn_list; ptr; ptr=ptr->next) { | ||
| 1079 | |||
| 1080 | if (ptr->msn == NULL) | ||
| 1081 | return 1; | ||
| 1082 | |||
| 1083 | if (strcmp(ptr->msn, msn) == 0) | ||
| 1084 | return 1; | ||
| 1085 | } | ||
| 1086 | |||
| 1087 | return 0; | ||
| 1088 | } | ||
diff --git a/drivers/isdn/pcbit/edss1.c b/drivers/isdn/pcbit/edss1.c new file mode 100644 index 000000000000..93ca7de5670b --- /dev/null +++ b/drivers/isdn/pcbit/edss1.c | |||
| @@ -0,0 +1,325 @@ | |||
| 1 | /* | ||
| 2 | * DSS.1 Finite State Machine | ||
| 3 | * base: ITU-T Rec Q.931 | ||
| 4 | * | ||
| 5 | * Copyright (C) 1996 Universidade de Lisboa | ||
| 6 | * | ||
| 7 | * Written by Pedro Roque Marques (roque@di.fc.ul.pt) | ||
| 8 | * | ||
| 9 | * This software may be used and distributed according to the terms of | ||
| 10 | * the GNU General Public License, incorporated herein by reference. | ||
| 11 | */ | ||
| 12 | |||
| 13 | /* | ||
| 14 | * TODO: complete the FSM | ||
| 15 | * move state/event descriptions to a user space logger | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/sched.h> | ||
| 19 | #include <linux/string.h> | ||
| 20 | #include <linux/kernel.h> | ||
| 21 | |||
| 22 | #include <linux/types.h> | ||
| 23 | #include <linux/slab.h> | ||
| 24 | #include <linux/mm.h> | ||
| 25 | #include <linux/skbuff.h> | ||
| 26 | |||
| 27 | #include <linux/timer.h> | ||
| 28 | #include <asm/io.h> | ||
| 29 | |||
| 30 | #include <linux/isdnif.h> | ||
| 31 | |||
| 32 | #include "pcbit.h" | ||
| 33 | #include "edss1.h" | ||
| 34 | #include "layer2.h" | ||
| 35 | #include "callbacks.h" | ||
| 36 | |||
| 37 | |||
| 38 | extern void pcbit_state_change(struct pcbit_dev *, struct pcbit_chan *, | ||
| 39 | unsigned short i, unsigned short ev, | ||
| 40 | unsigned short f); | ||
| 41 | |||
| 42 | extern struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS]; | ||
| 43 | |||
| 44 | char * isdn_state_table[] = { | ||
| 45 | "Closed", | ||
| 46 | "Call initiated", | ||
| 47 | "Overlap sending", | ||
| 48 | "Outgoing call proceeding", | ||
| 49 | "NOT DEFINED", | ||
| 50 | "Call delivered", | ||
| 51 | "Call present", | ||
| 52 | "Call received", | ||
| 53 | "Connect request", | ||
| 54 | "Incoming call proceeding", | ||
| 55 | "Active", | ||
| 56 | "Disconnect request", | ||
| 57 | "Disconnect indication", | ||
| 58 | "NOT DEFINED", | ||
| 59 | "NOT DEFINED", | ||
| 60 | "Suspend request", | ||
| 61 | "NOT DEFINED", | ||
| 62 | "Resume request", | ||
| 63 | "NOT DEFINED", | ||
| 64 | "Release Request", | ||
| 65 | "NOT DEFINED", | ||
| 66 | "NOT DEFINED", | ||
| 67 | "NOT DEFINED", | ||
| 68 | "NOT DEFINED", | ||
| 69 | "NOT DEFINED", | ||
| 70 | "Overlap receiving", | ||
| 71 | "Select protocol on B-Channel", | ||
| 72 | "Activate B-channel protocol" | ||
| 73 | }; | ||
| 74 | |||
| 75 | #ifdef DEBUG_ERRS | ||
| 76 | static | ||
| 77 | struct CauseValue { | ||
| 78 | byte nr; | ||
| 79 | char *descr; | ||
| 80 | } cvlist[]={ | ||
| 81 | {0x01,"Unallocated (unassigned) number"}, | ||
| 82 | {0x02,"No route to specified transit network"}, | ||
| 83 | {0x03,"No route to destination"}, | ||
| 84 | {0x04,"Send special information tone"}, | ||
| 85 | {0x05,"Misdialled trunk prefix"}, | ||
| 86 | {0x06,"Channel unacceptable"}, | ||
| 87 | {0x07,"Channel awarded and being delivered in an established channel"}, | ||
| 88 | {0x08,"Preemption"}, | ||
| 89 | {0x09,"Preemption - circuit reserved for reuse"}, | ||
| 90 | {0x10,"Normal call clearing"}, | ||
| 91 | {0x11,"User busy"}, | ||
| 92 | {0x12,"No user responding"}, | ||
| 93 | {0x13,"No answer from user (user alerted)"}, | ||
| 94 | {0x14,"Subscriber absent"}, | ||
| 95 | {0x15,"Call rejected"}, | ||
| 96 | {0x16,"Number changed"}, | ||
| 97 | {0x1a,"non-selected user clearing"}, | ||
| 98 | {0x1b,"Destination out of order"}, | ||
| 99 | {0x1c,"Invalid number format (address incomplete)"}, | ||
| 100 | {0x1d,"Facility rejected"}, | ||
| 101 | {0x1e,"Response to Status enquiry"}, | ||
| 102 | {0x1f,"Normal, unspecified"}, | ||
| 103 | {0x22,"No circuit/channel available"}, | ||
| 104 | {0x26,"Network out of order"}, | ||
| 105 | {0x27,"Permanent frame mode connection out-of-service"}, | ||
| 106 | {0x28,"Permanent frame mode connection operational"}, | ||
| 107 | {0x29,"Temporary failure"}, | ||
| 108 | {0x2a,"Switching equipment congestion"}, | ||
| 109 | {0x2b,"Access information discarded"}, | ||
| 110 | {0x2c,"Requested circuit/channel not available"}, | ||
| 111 | {0x2e,"Precedence call blocked"}, | ||
| 112 | {0x2f,"Resource unavailable, unspecified"}, | ||
| 113 | {0x31,"Quality of service unavailable"}, | ||
| 114 | {0x32,"Requested facility not subscribed"}, | ||
| 115 | {0x35,"Outgoing calls barred within CUG"}, | ||
| 116 | {0x37,"Incoming calls barred within CUG"}, | ||
| 117 | {0x39,"Bearer capability not authorized"}, | ||
| 118 | {0x3a,"Bearer capability not presently available"}, | ||
| 119 | {0x3e,"Inconsistency in designated outgoing access information and subscriber class"}, | ||
| 120 | {0x3f,"Service or option not available, unspecified"}, | ||
| 121 | {0x41,"Bearer capability not implemented"}, | ||
| 122 | {0x42,"Channel type not implemented"}, | ||
| 123 | {0x43,"Requested facility not implemented"}, | ||
| 124 | {0x44,"Only restricted digital information bearer capability is available"}, | ||
| 125 | {0x4f,"Service or option not implemented"}, | ||
| 126 | {0x51,"Invalid call reference value"}, | ||
| 127 | {0x52,"Identified channel does not exist"}, | ||
| 128 | {0x53,"A suspended call exists, but this call identity does not"}, | ||
| 129 | {0x54,"Call identity in use"}, | ||
| 130 | {0x55,"No call suspended"}, | ||
| 131 | {0x56,"Call having the requested call identity has been cleared"}, | ||
| 132 | {0x57,"User not member of CUG"}, | ||
| 133 | {0x58,"Incompatible destination"}, | ||
| 134 | {0x5a,"Non-existent CUG"}, | ||
| 135 | {0x5b,"Invalid transit network selection"}, | ||
| 136 | {0x5f,"Invalid message, unspecified"}, | ||
| 137 | {0x60,"Mandatory information element is missing"}, | ||
| 138 | {0x61,"Message type non-existent or not implemented"}, | ||
| 139 | {0x62,"Message not compatible with call state or message type non-existent or not implemented"}, | ||
| 140 | {0x63,"Information element/parameter non-existent or not implemented"}, | ||
| 141 | {0x64,"Invalid information element contents"}, | ||
| 142 | {0x65,"Message not compatible with call state"}, | ||
| 143 | {0x66,"Recovery on timer expiry"}, | ||
| 144 | {0x67,"Parameter non-existent or not implemented - passed on"}, | ||
| 145 | {0x6e,"Message with unrecognized parameter discarded"}, | ||
| 146 | {0x6f,"Protocol error, unspecified"}, | ||
| 147 | {0x7f,"Interworking, unspecified"} | ||
| 148 | }; | ||
| 149 | |||
| 150 | #endif | ||
| 151 | |||
| 152 | static struct isdn_event_desc { | ||
| 153 | unsigned short ev; | ||
| 154 | char * desc; | ||
| 155 | } isdn_event_table [] = { | ||
| 156 | {EV_USR_SETUP_REQ, "CC->L3: Setup Request"}, | ||
| 157 | {EV_USR_SETUP_RESP, "CC->L3: Setup Response"}, | ||
| 158 | {EV_USR_PROCED_REQ, "CC->L3: Proceeding Request"}, | ||
| 159 | {EV_USR_RELEASE_REQ, "CC->L3: Release Request"}, | ||
| 160 | |||
| 161 | {EV_NET_SETUP, "NET->TE: setup "}, | ||
| 162 | {EV_NET_CALL_PROC, "NET->TE: call proceeding"}, | ||
| 163 | {EV_NET_SETUP_ACK, "NET->TE: setup acknowledge (more info needed)"}, | ||
| 164 | {EV_NET_CONN, "NET->TE: connect"}, | ||
| 165 | {EV_NET_CONN_ACK, "NET->TE: connect acknowledge"}, | ||
| 166 | {EV_NET_DISC, "NET->TE: disconnect indication"}, | ||
| 167 | {EV_NET_RELEASE, "NET->TE: release"}, | ||
| 168 | {EV_NET_RELEASE_COMP, "NET->TE: release complete"}, | ||
| 169 | {EV_NET_SELP_RESP, "Board: Select B-channel protocol ack"}, | ||
| 170 | {EV_NET_ACTV_RESP, "Board: Activate B-channel protocol ack"}, | ||
| 171 | {EV_TIMER, "Timeout"}, | ||
| 172 | {0, "NULL"} | ||
| 173 | }; | ||
| 174 | |||
| 175 | char * strisdnevent(ushort ev) | ||
| 176 | { | ||
| 177 | struct isdn_event_desc * entry; | ||
| 178 | |||
| 179 | for (entry = isdn_event_table; entry->ev; entry++) | ||
| 180 | if (entry->ev == ev) | ||
| 181 | break; | ||
| 182 | |||
| 183 | return entry->desc; | ||
| 184 | } | ||
| 185 | |||
| 186 | /* | ||
| 187 | * Euro ISDN finite state machine | ||
| 188 | */ | ||
| 189 | |||
| 190 | static struct fsm_timer_entry fsm_timers[] = { | ||
| 191 | {ST_CALL_PROC, 10}, | ||
| 192 | {ST_DISC_REQ, 2}, | ||
| 193 | {ST_ACTIVE_SELP, 5}, | ||
| 194 | {ST_ACTIVE_ACTV, 5}, | ||
| 195 | {ST_INCM_PROC, 10}, | ||
| 196 | {ST_CONN_REQ, 2}, | ||
| 197 | {0xff, 0} | ||
| 198 | }; | ||
| 199 | |||
| 200 | static struct fsm_entry fsm_table[] = { | ||
| 201 | /* Connect Phase */ | ||
| 202 | /* Outgoing */ | ||
| 203 | {ST_NULL, ST_CALL_INIT, EV_USR_SETUP_REQ, cb_out_1}, | ||
| 204 | |||
| 205 | {ST_CALL_INIT, ST_OVER_SEND, EV_NET_SETUP_ACK, cb_notdone}, | ||
| 206 | {ST_CALL_INIT, ST_CALL_PROC, EV_NET_CALL_PROC, NULL}, | ||
| 207 | {ST_CALL_INIT, ST_NULL, EV_NET_DISC, cb_out_2}, | ||
| 208 | |||
| 209 | {ST_CALL_PROC, ST_ACTIVE_SELP, EV_NET_CONN, cb_out_2}, | ||
| 210 | {ST_CALL_PROC, ST_NULL, EV_NET_DISC, cb_disc_1}, | ||
| 211 | {ST_CALL_PROC, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2}, | ||
| 212 | |||
| 213 | /* Incoming */ | ||
| 214 | {ST_NULL, ST_CALL_PRES, EV_NET_SETUP, NULL}, | ||
| 215 | |||
| 216 | {ST_CALL_PRES, ST_INCM_PROC, EV_USR_PROCED_REQ, cb_in_1}, | ||
| 217 | {ST_CALL_PRES, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2}, | ||
| 218 | |||
| 219 | {ST_INCM_PROC, ST_CONN_REQ, EV_USR_SETUP_RESP, cb_in_2}, | ||
| 220 | {ST_INCM_PROC, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2}, | ||
| 221 | |||
| 222 | {ST_CONN_REQ, ST_ACTIVE_SELP, EV_NET_CONN_ACK, cb_in_3}, | ||
| 223 | |||
| 224 | /* Active */ | ||
| 225 | {ST_ACTIVE, ST_NULL, EV_NET_DISC, cb_disc_1}, | ||
| 226 | {ST_ACTIVE, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2}, | ||
| 227 | {ST_ACTIVE, ST_NULL, EV_NET_RELEASE, cb_disc_3}, | ||
| 228 | |||
| 229 | /* Disconnect */ | ||
| 230 | |||
| 231 | {ST_DISC_REQ, ST_NULL, EV_NET_DISC, cb_disc_1}, | ||
| 232 | {ST_DISC_REQ, ST_NULL, EV_NET_RELEASE, cb_disc_3}, | ||
| 233 | |||
| 234 | /* protocol selection */ | ||
| 235 | {ST_ACTIVE_SELP, ST_ACTIVE_ACTV, EV_NET_SELP_RESP, cb_selp_1}, | ||
| 236 | {ST_ACTIVE_SELP, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2}, | ||
| 237 | |||
| 238 | {ST_ACTIVE_ACTV, ST_ACTIVE, EV_NET_ACTV_RESP, cb_open}, | ||
| 239 | {ST_ACTIVE_ACTV, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2}, | ||
| 240 | |||
| 241 | /* Timers */ | ||
| 242 | {ST_CALL_PROC, ST_DISC_REQ, EV_TIMER, cb_disc_2}, | ||
| 243 | {ST_DISC_REQ, ST_NULL, EV_TIMER, cb_disc_3}, | ||
| 244 | {ST_ACTIVE_SELP, ST_DISC_REQ, EV_TIMER, cb_disc_2}, | ||
| 245 | {ST_ACTIVE_ACTV, ST_DISC_REQ, EV_TIMER, cb_disc_2}, | ||
| 246 | {ST_INCM_PROC, ST_DISC_REQ, EV_TIMER, cb_disc_2}, | ||
| 247 | {ST_CONN_REQ, ST_CONN_REQ, EV_TIMER, cb_in_2}, | ||
| 248 | |||
| 249 | {0xff, 0, 0, NULL} | ||
| 250 | }; | ||
| 251 | |||
| 252 | |||
| 253 | static void pcbit_fsm_timer(unsigned long data) | ||
| 254 | { | ||
| 255 | struct pcbit_dev *dev; | ||
| 256 | struct pcbit_chan *chan; | ||
| 257 | |||
| 258 | chan = (struct pcbit_chan *) data; | ||
| 259 | |||
| 260 | del_timer(&chan->fsm_timer); | ||
| 261 | chan->fsm_timer.function = NULL; | ||
| 262 | |||
| 263 | dev = chan2dev(chan); | ||
| 264 | |||
| 265 | if (dev == NULL) { | ||
| 266 | printk(KERN_WARNING "pcbit: timer for unknown device\n"); | ||
| 267 | return; | ||
| 268 | } | ||
| 269 | |||
| 270 | pcbit_fsm_event(dev, chan, EV_TIMER, NULL); | ||
| 271 | } | ||
| 272 | |||
| 273 | |||
| 274 | void pcbit_fsm_event(struct pcbit_dev *dev, struct pcbit_chan *chan, | ||
| 275 | unsigned short event, struct callb_data *data) | ||
| 276 | { | ||
| 277 | struct fsm_entry * action; | ||
| 278 | struct fsm_timer_entry *tentry; | ||
| 279 | unsigned long flags; | ||
| 280 | |||
| 281 | spin_lock_irqsave(&dev->lock, flags); | ||
| 282 | |||
| 283 | for (action = fsm_table; action->init != 0xff; action++) | ||
| 284 | if (action->init == chan->fsm_state && action->event == event) | ||
| 285 | break; | ||
| 286 | |||
| 287 | if (action->init == 0xff) { | ||
| 288 | |||
| 289 | spin_unlock_irqrestore(&dev->lock, flags); | ||
| 290 | printk(KERN_DEBUG "fsm error: event %x on state %x\n", | ||
| 291 | event, chan->fsm_state); | ||
| 292 | return; | ||
| 293 | } | ||
| 294 | |||
| 295 | if (chan->fsm_timer.function) { | ||
| 296 | del_timer(&chan->fsm_timer); | ||
| 297 | chan->fsm_timer.function = NULL; | ||
| 298 | } | ||
| 299 | |||
| 300 | chan->fsm_state = action->final; | ||
| 301 | |||
| 302 | pcbit_state_change(dev, chan, action->init, event, action->final); | ||
| 303 | |||
| 304 | for (tentry = fsm_timers; tentry->init != 0xff; tentry++) | ||
| 305 | if (tentry->init == chan->fsm_state) | ||
| 306 | break; | ||
| 307 | |||
| 308 | if (tentry->init != 0xff) { | ||
| 309 | init_timer(&chan->fsm_timer); | ||
| 310 | chan->fsm_timer.function = &pcbit_fsm_timer; | ||
| 311 | chan->fsm_timer.data = (ulong) chan; | ||
| 312 | chan->fsm_timer.expires = jiffies + tentry->timeout * HZ; | ||
| 313 | add_timer(&chan->fsm_timer); | ||
| 314 | } | ||
| 315 | |||
| 316 | spin_unlock_irqrestore(&dev->lock, flags); | ||
| 317 | |||
| 318 | if (action->callb) | ||
| 319 | action->callb(dev, chan, data); | ||
| 320 | |||
| 321 | } | ||
| 322 | |||
| 323 | |||
| 324 | |||
| 325 | |||
diff --git a/drivers/isdn/pcbit/edss1.h b/drivers/isdn/pcbit/edss1.h new file mode 100644 index 000000000000..6bb587005b86 --- /dev/null +++ b/drivers/isdn/pcbit/edss1.h | |||
| @@ -0,0 +1,99 @@ | |||
| 1 | /* | ||
| 2 | * DSS.1 module definitions | ||
| 3 | * | ||
| 4 | * Copyright (C) 1996 Universidade de Lisboa | ||
| 5 | * | ||
| 6 | * Written by Pedro Roque Marques (roque@di.fc.ul.pt) | ||
| 7 | * | ||
| 8 | * This software may be used and distributed according to the terms of | ||
| 9 | * the GNU General Public License, incorporated herein by reference. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef EDSS1_H | ||
| 13 | #define EDSS1_H | ||
| 14 | |||
| 15 | /* ISDN states */ | ||
| 16 | |||
| 17 | #define ST_NULL 0 | ||
| 18 | #define ST_CALL_INIT 1 /* Call initiated */ | ||
| 19 | #define ST_OVER_SEND 2 /* Overlap sending - Requests More Info 4 call */ | ||
| 20 | #define ST_CALL_PROC 3 /* Call Proceeding */ | ||
| 21 | #define ST_CALL_DELV 4 | ||
| 22 | #define ST_CALL_PRES 6 /* Call Present - Received CONN.IND */ | ||
| 23 | #define ST_CALL_RECV 7 /* Alerting sent */ | ||
| 24 | #define ST_CONN_REQ 8 /* Answered - waiting 4 CONN.CONF */ | ||
| 25 | #define ST_INCM_PROC 9 | ||
| 26 | #define ST_ACTIVE 10 | ||
| 27 | #define ST_DISC_REQ 11 | ||
| 28 | #define ST_DISC_IND 12 | ||
| 29 | #define ST_SUSP_REQ 15 | ||
| 30 | #define ST_RESM_REQ 17 | ||
| 31 | #define ST_RELS_REQ 19 | ||
| 32 | #define ST_OVER_RECV 25 | ||
| 33 | |||
| 34 | #define ST_ACTIVE_SELP 26 /* Select protocol on B-Channel */ | ||
| 35 | #define ST_ACTIVE_ACTV 27 /* Activate B-channel protocol */ | ||
| 36 | |||
| 37 | #define MAX_STATE ST_ACTIVE_ACTV | ||
| 38 | |||
| 39 | #define EV_NULL 0 | ||
| 40 | #define EV_USR_SETUP_REQ 1 | ||
| 41 | #define EV_USR_SETUP_RESP 2 | ||
| 42 | #define EV_USR_PROCED_REQ 3 | ||
| 43 | #define EV_USR_RELEASE_REQ 4 | ||
| 44 | #define EV_USR_REJECT_REQ 4 | ||
| 45 | |||
| 46 | #define EV_NET_SETUP 16 | ||
| 47 | #define EV_NET_CALL_PROC 17 | ||
| 48 | #define EV_NET_SETUP_ACK 18 | ||
| 49 | #define EV_NET_CONN 19 | ||
| 50 | #define EV_NET_CONN_ACK 20 | ||
| 51 | |||
| 52 | #define EV_NET_SELP_RESP 21 | ||
| 53 | #define EV_NET_ACTV_RESP 22 | ||
| 54 | |||
| 55 | #define EV_NET_DISC 23 | ||
| 56 | #define EV_NET_RELEASE 24 | ||
| 57 | #define EV_NET_RELEASE_COMP 25 | ||
| 58 | |||
| 59 | #define EV_TIMER 26 | ||
| 60 | #define EV_ERROR 32 | ||
| 61 | |||
| 62 | /* | ||
| 63 | * Cause values | ||
| 64 | * only the ones we use | ||
| 65 | */ | ||
| 66 | |||
| 67 | #define CAUSE_NORMAL 0x10U | ||
| 68 | #define CAUSE_NOCHAN 0x22U | ||
| 69 | |||
| 70 | struct callb_data { | ||
| 71 | unsigned short type; | ||
| 72 | union { | ||
| 73 | struct ConnInfo { | ||
| 74 | char *CalledPN; | ||
| 75 | char *CallingPN; | ||
| 76 | } setup; | ||
| 77 | unsigned short cause; | ||
| 78 | } data; | ||
| 79 | }; | ||
| 80 | |||
| 81 | struct fsm_entry { | ||
| 82 | unsigned short init; | ||
| 83 | unsigned short final; | ||
| 84 | unsigned short event; | ||
| 85 | void (*callb)(struct pcbit_dev *, struct pcbit_chan *, struct callb_data*); | ||
| 86 | }; | ||
| 87 | |||
| 88 | struct fsm_timer_entry { | ||
| 89 | unsigned short init; | ||
| 90 | unsigned long timeout; /* in seconds */ | ||
| 91 | }; | ||
| 92 | |||
| 93 | |||
| 94 | extern void pcbit_fsm_event(struct pcbit_dev *, struct pcbit_chan *, | ||
| 95 | unsigned short event, struct callb_data *); | ||
| 96 | #endif | ||
| 97 | |||
| 98 | |||
| 99 | |||
diff --git a/drivers/isdn/pcbit/layer2.c b/drivers/isdn/pcbit/layer2.c new file mode 100644 index 000000000000..ba766930f088 --- /dev/null +++ b/drivers/isdn/pcbit/layer2.c | |||
| @@ -0,0 +1,732 @@ | |||
| 1 | /* | ||
| 2 | * PCBIT-D low-layer interface | ||
| 3 | * | ||
| 4 | * Copyright (C) 1996 Universidade de Lisboa | ||
| 5 | * | ||
| 6 | * Written by Pedro Roque Marques (roque@di.fc.ul.pt) | ||
| 7 | * | ||
| 8 | * This software may be used and distributed according to the terms of | ||
| 9 | * the GNU General Public License, incorporated herein by reference. | ||
| 10 | */ | ||
| 11 | |||
| 12 | /* | ||
| 13 | * 19991203 - Fernando Carvalho - takion@superbofh.org | ||
| 14 | * Hacked to compile with egcs and run with current version of isdn modules | ||
| 15 | */ | ||
| 16 | |||
| 17 | /* | ||
| 18 | * Based on documentation provided by Inesc: | ||
| 19 | * - "Interface com bus do PC para o PCBIT e PCBIT-D", Inesc, Jan 93 | ||
| 20 | */ | ||
| 21 | |||
| 22 | /* | ||
| 23 | * TODO: better handling of errors | ||
| 24 | * re-write/remove debug printks | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include <linux/sched.h> | ||
| 28 | #include <linux/string.h> | ||
| 29 | #include <linux/kernel.h> | ||
| 30 | #include <linux/types.h> | ||
| 31 | #include <linux/slab.h> | ||
| 32 | #include <linux/interrupt.h> | ||
| 33 | #include <linux/workqueue.h> | ||
| 34 | #include <linux/mm.h> | ||
| 35 | #include <linux/skbuff.h> | ||
| 36 | |||
| 37 | #include <linux/isdnif.h> | ||
| 38 | |||
| 39 | #include <asm/system.h> | ||
| 40 | #include <asm/io.h> | ||
| 41 | |||
| 42 | |||
| 43 | #include "pcbit.h" | ||
| 44 | #include "layer2.h" | ||
| 45 | #include "edss1.h" | ||
| 46 | |||
| 47 | #undef DEBUG_FRAG | ||
| 48 | |||
| 49 | |||
| 50 | |||
| 51 | /* | ||
| 52 | * task queue struct | ||
| 53 | */ | ||
| 54 | |||
| 55 | |||
| 56 | |||
| 57 | /* | ||
| 58 | * Layer 3 packet demultiplexer | ||
| 59 | * drv.c | ||
| 60 | */ | ||
| 61 | |||
| 62 | extern void pcbit_l3_receive(struct pcbit_dev *dev, ulong msg, | ||
| 63 | struct sk_buff *skb, | ||
| 64 | ushort hdr_len, ushort refnum); | ||
| 65 | |||
| 66 | /* | ||
| 67 | * Prototypes | ||
| 68 | */ | ||
| 69 | |||
| 70 | void pcbit_deliver(void *data); | ||
| 71 | static void pcbit_transmit(struct pcbit_dev *dev); | ||
| 72 | |||
| 73 | static void pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack); | ||
| 74 | |||
| 75 | static void pcbit_l2_error(struct pcbit_dev *dev); | ||
| 76 | static void pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info); | ||
| 77 | static void pcbit_l2_err_recover(unsigned long data); | ||
| 78 | |||
| 79 | static void pcbit_firmware_bug(struct pcbit_dev *dev); | ||
| 80 | |||
| 81 | static __inline__ void | ||
| 82 | pcbit_sched_delivery(struct pcbit_dev *dev) | ||
| 83 | { | ||
| 84 | schedule_work(&dev->qdelivery); | ||
| 85 | } | ||
| 86 | |||
| 87 | |||
| 88 | /* | ||
| 89 | * Called from layer3 | ||
| 90 | */ | ||
| 91 | |||
| 92 | int | ||
| 93 | pcbit_l2_write(struct pcbit_dev *dev, ulong msg, ushort refnum, | ||
| 94 | struct sk_buff *skb, unsigned short hdr_len) | ||
| 95 | { | ||
| 96 | struct frame_buf *frame, | ||
| 97 | *ptr; | ||
| 98 | unsigned long flags; | ||
| 99 | |||
| 100 | if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) { | ||
| 101 | dev_kfree_skb(skb); | ||
| 102 | return -1; | ||
| 103 | } | ||
| 104 | if ((frame = (struct frame_buf *) kmalloc(sizeof(struct frame_buf), | ||
| 105 | GFP_ATOMIC)) == NULL) { | ||
| 106 | printk(KERN_WARNING "pcbit_2_write: kmalloc failed\n"); | ||
| 107 | dev_kfree_skb(skb); | ||
| 108 | return -1; | ||
| 109 | } | ||
| 110 | frame->msg = msg; | ||
| 111 | frame->refnum = refnum; | ||
| 112 | frame->copied = 0; | ||
| 113 | frame->hdr_len = hdr_len; | ||
| 114 | |||
| 115 | if (skb) | ||
| 116 | frame->dt_len = skb->len - hdr_len; | ||
| 117 | else | ||
| 118 | frame->dt_len = 0; | ||
| 119 | |||
| 120 | frame->skb = skb; | ||
| 121 | |||
| 122 | frame->next = NULL; | ||
| 123 | |||
| 124 | spin_lock_irqsave(&dev->lock, flags); | ||
| 125 | |||
| 126 | if (dev->write_queue == NULL) { | ||
| 127 | dev->write_queue = frame; | ||
| 128 | spin_unlock_irqrestore(&dev->lock, flags); | ||
| 129 | pcbit_transmit(dev); | ||
| 130 | } else { | ||
| 131 | for (ptr = dev->write_queue; ptr->next; ptr = ptr->next); | ||
| 132 | ptr->next = frame; | ||
| 133 | |||
| 134 | spin_unlock_irqrestore(&dev->lock, flags); | ||
| 135 | } | ||
| 136 | return 0; | ||
| 137 | } | ||
| 138 | |||
| 139 | static __inline__ void | ||
| 140 | pcbit_tx_update(struct pcbit_dev *dev, ushort len) | ||
| 141 | { | ||
| 142 | u_char info; | ||
| 143 | |||
| 144 | dev->send_seq = (dev->send_seq + 1) % 8; | ||
| 145 | |||
| 146 | dev->fsize[dev->send_seq] = len; | ||
| 147 | info = 0; | ||
| 148 | info |= dev->rcv_seq << 3; | ||
| 149 | info |= dev->send_seq; | ||
| 150 | |||
| 151 | writeb(info, dev->sh_mem + BANK4); | ||
| 152 | |||
| 153 | } | ||
| 154 | |||
| 155 | /* | ||
| 156 | * called by interrupt service routine or by write_2 | ||
| 157 | */ | ||
| 158 | |||
| 159 | static void | ||
| 160 | pcbit_transmit(struct pcbit_dev *dev) | ||
| 161 | { | ||
| 162 | struct frame_buf *frame = NULL; | ||
| 163 | unsigned char unacked; | ||
| 164 | int flen; /* fragment frame length including all headers */ | ||
| 165 | int free; | ||
| 166 | int count, | ||
| 167 | cp_len; | ||
| 168 | unsigned long flags; | ||
| 169 | unsigned short tt; | ||
| 170 | |||
| 171 | if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) | ||
| 172 | return; | ||
| 173 | |||
| 174 | unacked = (dev->send_seq + (8 - dev->unack_seq)) & 0x07; | ||
| 175 | |||
| 176 | spin_lock_irqsave(&dev->lock, flags); | ||
| 177 | |||
| 178 | if (dev->free > 16 && dev->write_queue && unacked < 7) { | ||
| 179 | |||
| 180 | if (!dev->w_busy) | ||
| 181 | dev->w_busy = 1; | ||
| 182 | else { | ||
| 183 | spin_unlock_irqrestore(&dev->lock, flags); | ||
| 184 | return; | ||
| 185 | } | ||
| 186 | |||
| 187 | |||
| 188 | frame = dev->write_queue; | ||
| 189 | free = dev->free; | ||
| 190 | |||
| 191 | spin_unlock_irqrestore(&dev->lock, flags); | ||
| 192 | |||
| 193 | if (frame->copied == 0) { | ||
| 194 | |||
| 195 | /* Type 0 frame */ | ||
| 196 | |||
| 197 | ulong msg; | ||
| 198 | |||
| 199 | if (frame->skb) | ||
| 200 | flen = FRAME_HDR_LEN + PREHDR_LEN + frame->skb->len; | ||
| 201 | else | ||
| 202 | flen = FRAME_HDR_LEN + PREHDR_LEN; | ||
| 203 | |||
| 204 | if (flen > free) | ||
| 205 | flen = free; | ||
| 206 | |||
| 207 | msg = frame->msg; | ||
| 208 | |||
| 209 | /* | ||
| 210 | * Board level 2 header | ||
| 211 | */ | ||
| 212 | |||
| 213 | pcbit_writew(dev, flen - FRAME_HDR_LEN); | ||
| 214 | |||
| 215 | pcbit_writeb(dev, GET_MSG_CPU(msg)); | ||
| 216 | |||
| 217 | pcbit_writeb(dev, GET_MSG_PROC(msg)); | ||
| 218 | |||
| 219 | /* TH */ | ||
| 220 | pcbit_writew(dev, frame->hdr_len + PREHDR_LEN); | ||
| 221 | |||
| 222 | /* TD */ | ||
| 223 | pcbit_writew(dev, frame->dt_len); | ||
| 224 | |||
| 225 | |||
| 226 | /* | ||
| 227 | * Board level 3 fixed-header | ||
| 228 | */ | ||
| 229 | |||
| 230 | /* LEN = TH */ | ||
| 231 | pcbit_writew(dev, frame->hdr_len + PREHDR_LEN); | ||
| 232 | |||
| 233 | /* XX */ | ||
| 234 | pcbit_writew(dev, 0); | ||
| 235 | |||
| 236 | /* C + S */ | ||
| 237 | pcbit_writeb(dev, GET_MSG_CMD(msg)); | ||
| 238 | pcbit_writeb(dev, GET_MSG_SCMD(msg)); | ||
| 239 | |||
| 240 | /* NUM */ | ||
| 241 | pcbit_writew(dev, frame->refnum); | ||
| 242 | |||
| 243 | count = FRAME_HDR_LEN + PREHDR_LEN; | ||
| 244 | } else { | ||
| 245 | /* Type 1 frame */ | ||
| 246 | |||
| 247 | flen = 2 + (frame->skb->len - frame->copied); | ||
| 248 | |||
| 249 | if (flen > free) | ||
| 250 | flen = free; | ||
| 251 | |||
| 252 | /* TT */ | ||
| 253 | tt = ((ushort) (flen - 2)) | 0x8000U; /* Type 1 */ | ||
| 254 | pcbit_writew(dev, tt); | ||
| 255 | |||
| 256 | count = 2; | ||
| 257 | } | ||
| 258 | |||
| 259 | if (frame->skb) { | ||
| 260 | cp_len = frame->skb->len - frame->copied; | ||
| 261 | if (cp_len > flen - count) | ||
| 262 | cp_len = flen - count; | ||
| 263 | |||
| 264 | memcpy_topcbit(dev, frame->skb->data + frame->copied, | ||
| 265 | cp_len); | ||
| 266 | frame->copied += cp_len; | ||
| 267 | } | ||
| 268 | /* bookkeeping */ | ||
| 269 | dev->free -= flen; | ||
| 270 | pcbit_tx_update(dev, flen); | ||
| 271 | |||
| 272 | spin_lock_irqsave(&dev->lock, flags); | ||
| 273 | |||
| 274 | if (frame->skb == NULL || frame->copied == frame->skb->len) { | ||
| 275 | |||
| 276 | dev->write_queue = frame->next; | ||
| 277 | |||
| 278 | if (frame->skb != NULL) { | ||
| 279 | /* free frame */ | ||
| 280 | dev_kfree_skb(frame->skb); | ||
| 281 | } | ||
| 282 | kfree(frame); | ||
| 283 | } | ||
| 284 | dev->w_busy = 0; | ||
| 285 | spin_unlock_irqrestore(&dev->lock, flags); | ||
| 286 | } else { | ||
| 287 | spin_unlock_irqrestore(&dev->lock, flags); | ||
| 288 | #ifdef DEBUG | ||
| 289 | printk(KERN_DEBUG "unacked %d free %d write_queue %s\n", | ||
| 290 | unacked, dev->free, dev->write_queue ? "not empty" : | ||
| 291 | "empty"); | ||
| 292 | #endif | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | |||
| 297 | /* | ||
| 298 | * deliver a queued frame to the upper layer | ||
| 299 | */ | ||
| 300 | |||
| 301 | void | ||
| 302 | pcbit_deliver(void *data) | ||
| 303 | { | ||
| 304 | struct frame_buf *frame; | ||
| 305 | unsigned long flags, msg; | ||
| 306 | struct pcbit_dev *dev = (struct pcbit_dev *) data; | ||
| 307 | |||
| 308 | spin_lock_irqsave(&dev->lock, flags); | ||
| 309 | |||
| 310 | while ((frame = dev->read_queue)) { | ||
| 311 | dev->read_queue = frame->next; | ||
| 312 | spin_unlock_irqrestore(&dev->lock, flags); | ||
| 313 | |||
| 314 | SET_MSG_CPU(msg, 0); | ||
| 315 | SET_MSG_PROC(msg, 0); | ||
| 316 | SET_MSG_CMD(msg, frame->skb->data[2]); | ||
| 317 | SET_MSG_SCMD(msg, frame->skb->data[3]); | ||
| 318 | |||
| 319 | frame->refnum = *((ushort *) frame->skb->data + 4); | ||
| 320 | frame->msg = *((ulong *) & msg); | ||
| 321 | |||
| 322 | skb_pull(frame->skb, 6); | ||
| 323 | |||
| 324 | pcbit_l3_receive(dev, frame->msg, frame->skb, frame->hdr_len, | ||
| 325 | frame->refnum); | ||
| 326 | |||
| 327 | kfree(frame); | ||
| 328 | |||
| 329 | spin_lock_irqsave(&dev->lock, flags); | ||
| 330 | } | ||
| 331 | |||
| 332 | spin_unlock_irqrestore(&dev->lock, flags); | ||
| 333 | } | ||
| 334 | |||
| 335 | /* | ||
| 336 | * Reads BANK 2 & Reassembles | ||
| 337 | */ | ||
| 338 | |||
| 339 | static void | ||
| 340 | pcbit_receive(struct pcbit_dev *dev) | ||
| 341 | { | ||
| 342 | unsigned short tt; | ||
| 343 | u_char cpu, | ||
| 344 | proc; | ||
| 345 | struct frame_buf *frame = NULL; | ||
| 346 | unsigned long flags; | ||
| 347 | u_char type1; | ||
| 348 | |||
| 349 | if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) | ||
| 350 | return; | ||
| 351 | |||
| 352 | tt = pcbit_readw(dev); | ||
| 353 | |||
| 354 | if ((tt & 0x7fffU) > 511) { | ||
| 355 | printk(KERN_INFO "pcbit: invalid frame length -> TT=%04x\n", | ||
| 356 | tt); | ||
| 357 | pcbit_l2_error(dev); | ||
| 358 | return; | ||
| 359 | } | ||
| 360 | if (!(tt & 0x8000U)) { /* Type 0 */ | ||
| 361 | type1 = 0; | ||
| 362 | |||
| 363 | if (dev->read_frame) { | ||
| 364 | printk(KERN_DEBUG "pcbit_receive: Type 0 frame and read_frame != NULL\n"); | ||
| 365 | /* discard previous queued frame */ | ||
| 366 | if (dev->read_frame->skb) | ||
| 367 | kfree_skb(dev->read_frame->skb); | ||
| 368 | kfree(dev->read_frame); | ||
| 369 | dev->read_frame = NULL; | ||
| 370 | } | ||
| 371 | frame = kmalloc(sizeof(struct frame_buf), GFP_ATOMIC); | ||
| 372 | |||
| 373 | if (frame == NULL) { | ||
| 374 | printk(KERN_WARNING "kmalloc failed\n"); | ||
| 375 | return; | ||
| 376 | } | ||
| 377 | memset(frame, 0, sizeof(struct frame_buf)); | ||
| 378 | |||
| 379 | cpu = pcbit_readb(dev); | ||
| 380 | proc = pcbit_readb(dev); | ||
| 381 | |||
| 382 | |||
| 383 | if (cpu != 0x06 && cpu != 0x02) { | ||
| 384 | printk(KERN_DEBUG "pcbit: invalid cpu value\n"); | ||
| 385 | kfree(frame); | ||
| 386 | pcbit_l2_error(dev); | ||
| 387 | return; | ||
| 388 | } | ||
| 389 | /* | ||
| 390 | * we discard cpu & proc on receiving | ||
| 391 | * but we read it to update the pointer | ||
| 392 | */ | ||
| 393 | |||
| 394 | frame->hdr_len = pcbit_readw(dev); | ||
| 395 | frame->dt_len = pcbit_readw(dev); | ||
| 396 | |||
| 397 | /* | ||
| 398 | * 0 sized packet | ||
| 399 | * I don't know if they are an error or not... | ||
| 400 | * But they are very frequent | ||
| 401 | * Not documented | ||
| 402 | */ | ||
| 403 | |||
| 404 | if (frame->hdr_len == 0) { | ||
| 405 | kfree(frame); | ||
| 406 | #ifdef DEBUG | ||
| 407 | printk(KERN_DEBUG "0 sized frame\n"); | ||
| 408 | #endif | ||
| 409 | pcbit_firmware_bug(dev); | ||
| 410 | return; | ||
| 411 | } | ||
| 412 | /* sanity check the length values */ | ||
| 413 | if (frame->hdr_len > 1024 || frame->dt_len > 2048) { | ||
| 414 | #ifdef DEBUG | ||
| 415 | printk(KERN_DEBUG "length problem: "); | ||
| 416 | printk(KERN_DEBUG "TH=%04x TD=%04x\n", | ||
| 417 | frame->hdr_len, | ||
| 418 | frame->dt_len); | ||
| 419 | #endif | ||
| 420 | pcbit_l2_error(dev); | ||
| 421 | kfree(frame); | ||
| 422 | return; | ||
| 423 | } | ||
| 424 | /* minimum frame read */ | ||
| 425 | |||
| 426 | frame->skb = dev_alloc_skb(frame->hdr_len + frame->dt_len + | ||
| 427 | ((frame->hdr_len + 15) & ~15)); | ||
| 428 | |||
| 429 | if (!frame->skb) { | ||
| 430 | printk(KERN_DEBUG "pcbit_receive: out of memory\n"); | ||
| 431 | kfree(frame); | ||
| 432 | return; | ||
| 433 | } | ||
| 434 | /* 16 byte alignment for IP */ | ||
| 435 | if (frame->dt_len) | ||
| 436 | skb_reserve(frame->skb, (frame->hdr_len + 15) & ~15); | ||
| 437 | |||
| 438 | } else { | ||
| 439 | /* Type 1 */ | ||
| 440 | type1 = 1; | ||
| 441 | tt &= 0x7fffU; | ||
| 442 | |||
| 443 | if (!(frame = dev->read_frame)) { | ||
| 444 | printk("Type 1 frame and no frame queued\n"); | ||
| 445 | /* usually after an error: toss frame */ | ||
| 446 | dev->readptr += tt; | ||
| 447 | if (dev->readptr > dev->sh_mem + BANK2 + BANKLEN) | ||
| 448 | dev->readptr -= BANKLEN; | ||
| 449 | return; | ||
| 450 | |||
| 451 | } | ||
| 452 | } | ||
| 453 | |||
| 454 | memcpy_frompcbit(dev, skb_put(frame->skb, tt), tt); | ||
| 455 | |||
| 456 | frame->copied += tt; | ||
| 457 | spin_lock_irqsave(&dev->lock, flags); | ||
| 458 | if (frame->copied == frame->hdr_len + frame->dt_len) { | ||
| 459 | |||
| 460 | if (type1) { | ||
| 461 | dev->read_frame = NULL; | ||
| 462 | } | ||
| 463 | if (dev->read_queue) { | ||
| 464 | struct frame_buf *ptr; | ||
| 465 | for (ptr = dev->read_queue; ptr->next; ptr = ptr->next); | ||
| 466 | ptr->next = frame; | ||
| 467 | } else | ||
| 468 | dev->read_queue = frame; | ||
| 469 | |||
| 470 | } else { | ||
| 471 | dev->read_frame = frame; | ||
| 472 | } | ||
| 473 | spin_unlock_irqrestore(&dev->lock, flags); | ||
| 474 | } | ||
| 475 | |||
| 476 | /* | ||
| 477 | * The board sends 0 sized frames | ||
| 478 | * They are TDATA_CONFs that get messed up somehow | ||
| 479 | * gotta send a fake acknowledgment to the upper layer somehow | ||
| 480 | */ | ||
| 481 | |||
| 482 | static __inline__ void | ||
| 483 | pcbit_fake_conf(struct pcbit_dev *dev, struct pcbit_chan *chan) | ||
| 484 | { | ||
| 485 | isdn_ctrl ictl; | ||
| 486 | |||
| 487 | if (chan->queued) { | ||
| 488 | chan->queued--; | ||
| 489 | |||
| 490 | ictl.driver = dev->id; | ||
| 491 | ictl.command = ISDN_STAT_BSENT; | ||
| 492 | ictl.arg = chan->id; | ||
| 493 | dev->dev_if->statcallb(&ictl); | ||
| 494 | } | ||
| 495 | } | ||
| 496 | |||
| 497 | static void | ||
| 498 | pcbit_firmware_bug(struct pcbit_dev *dev) | ||
| 499 | { | ||
| 500 | struct pcbit_chan *chan; | ||
| 501 | |||
| 502 | chan = dev->b1; | ||
| 503 | |||
| 504 | if (chan->fsm_state == ST_ACTIVE) { | ||
| 505 | pcbit_fake_conf(dev, chan); | ||
| 506 | } | ||
| 507 | chan = dev->b2; | ||
| 508 | |||
| 509 | if (chan->fsm_state == ST_ACTIVE) { | ||
| 510 | pcbit_fake_conf(dev, chan); | ||
| 511 | } | ||
| 512 | } | ||
| 513 | |||
| 514 | irqreturn_t | ||
| 515 | pcbit_irq_handler(int interrupt, void *devptr, struct pt_regs *regs) | ||
| 516 | { | ||
| 517 | struct pcbit_dev *dev; | ||
| 518 | u_char info, | ||
| 519 | ack_seq, | ||
| 520 | read_seq; | ||
| 521 | |||
| 522 | dev = (struct pcbit_dev *) devptr; | ||
| 523 | |||
| 524 | if (!dev) { | ||
| 525 | printk(KERN_WARNING "pcbit_irq_handler: wrong device\n"); | ||
| 526 | return IRQ_NONE; | ||
| 527 | } | ||
| 528 | if (dev->interrupt) { | ||
| 529 | printk(KERN_DEBUG "pcbit: reentering interrupt hander\n"); | ||
| 530 | return IRQ_HANDLED; | ||
| 531 | } | ||
| 532 | dev->interrupt = 1; | ||
| 533 | |||
| 534 | info = readb(dev->sh_mem + BANK3); | ||
| 535 | |||
| 536 | if (dev->l2_state == L2_STARTING || dev->l2_state == L2_ERROR) { | ||
| 537 | pcbit_l2_active_conf(dev, info); | ||
| 538 | dev->interrupt = 0; | ||
| 539 | return IRQ_HANDLED; | ||
| 540 | } | ||
| 541 | if (info & 0x40U) { /* E bit set */ | ||
| 542 | #ifdef DEBUG | ||
| 543 | printk(KERN_DEBUG "pcbit_irq_handler: E bit on\n"); | ||
| 544 | #endif | ||
| 545 | pcbit_l2_error(dev); | ||
| 546 | dev->interrupt = 0; | ||
| 547 | return IRQ_HANDLED; | ||
| 548 | } | ||
| 549 | if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) { | ||
| 550 | dev->interrupt = 0; | ||
| 551 | return IRQ_HANDLED; | ||
| 552 | } | ||
| 553 | ack_seq = (info >> 3) & 0x07U; | ||
| 554 | read_seq = (info & 0x07U); | ||
| 555 | |||
| 556 | dev->interrupt = 0; | ||
| 557 | |||
| 558 | if (read_seq != dev->rcv_seq) { | ||
| 559 | while (read_seq != dev->rcv_seq) { | ||
| 560 | pcbit_receive(dev); | ||
| 561 | dev->rcv_seq = (dev->rcv_seq + 1) % 8; | ||
| 562 | } | ||
| 563 | pcbit_sched_delivery(dev); | ||
| 564 | } | ||
| 565 | if (ack_seq != dev->unack_seq) { | ||
| 566 | pcbit_recv_ack(dev, ack_seq); | ||
| 567 | } | ||
| 568 | info = dev->rcv_seq << 3; | ||
| 569 | info |= dev->send_seq; | ||
| 570 | |||
| 571 | writeb(info, dev->sh_mem + BANK4); | ||
| 572 | return IRQ_HANDLED; | ||
| 573 | } | ||
| 574 | |||
| 575 | |||
| 576 | static void | ||
| 577 | pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info) | ||
| 578 | { | ||
| 579 | u_char state; | ||
| 580 | |||
| 581 | state = dev->l2_state; | ||
| 582 | |||
| 583 | #ifdef DEBUG | ||
| 584 | printk(KERN_DEBUG "layer2_active_confirm\n"); | ||
| 585 | #endif | ||
| 586 | |||
| 587 | |||
| 588 | if (info & 0x80U) { | ||
| 589 | dev->rcv_seq = info & 0x07U; | ||
| 590 | dev->l2_state = L2_RUNNING; | ||
| 591 | } else | ||
| 592 | dev->l2_state = L2_DOWN; | ||
| 593 | |||
| 594 | if (state == L2_STARTING) | ||
| 595 | wake_up_interruptible(&dev->set_running_wq); | ||
| 596 | |||
| 597 | if (state == L2_ERROR && dev->l2_state == L2_RUNNING) { | ||
| 598 | pcbit_transmit(dev); | ||
| 599 | } | ||
| 600 | } | ||
| 601 | |||
| 602 | static void | ||
| 603 | pcbit_l2_err_recover(unsigned long data) | ||
| 604 | { | ||
| 605 | |||
| 606 | struct pcbit_dev *dev; | ||
| 607 | struct frame_buf *frame; | ||
| 608 | |||
| 609 | dev = (struct pcbit_dev *) data; | ||
| 610 | |||
| 611 | del_timer(&dev->error_recover_timer); | ||
| 612 | if (dev->w_busy || dev->r_busy) { | ||
| 613 | init_timer(&dev->error_recover_timer); | ||
| 614 | dev->error_recover_timer.expires = jiffies + ERRTIME; | ||
| 615 | add_timer(&dev->error_recover_timer); | ||
| 616 | return; | ||
| 617 | } | ||
| 618 | dev->w_busy = dev->r_busy = 1; | ||
| 619 | |||
| 620 | if (dev->read_frame) { | ||
| 621 | if (dev->read_frame->skb) | ||
| 622 | kfree_skb(dev->read_frame->skb); | ||
| 623 | kfree(dev->read_frame); | ||
| 624 | dev->read_frame = NULL; | ||
| 625 | } | ||
| 626 | if (dev->write_queue) { | ||
| 627 | frame = dev->write_queue; | ||
| 628 | #ifdef FREE_ON_ERROR | ||
| 629 | dev->write_queue = dev->write_queue->next; | ||
| 630 | |||
| 631 | if (frame->skb) { | ||
| 632 | dev_kfree_skb(frame->skb); | ||
| 633 | } | ||
| 634 | kfree(frame); | ||
| 635 | #else | ||
| 636 | frame->copied = 0; | ||
| 637 | #endif | ||
| 638 | } | ||
| 639 | dev->rcv_seq = dev->send_seq = dev->unack_seq = 0; | ||
| 640 | dev->free = 511; | ||
| 641 | dev->l2_state = L2_ERROR; | ||
| 642 | |||
| 643 | /* this is an hack... */ | ||
| 644 | pcbit_firmware_bug(dev); | ||
| 645 | |||
| 646 | dev->writeptr = dev->sh_mem; | ||
| 647 | dev->readptr = dev->sh_mem + BANK2; | ||
| 648 | |||
| 649 | writeb((0x80U | ((dev->rcv_seq & 0x07) << 3) | (dev->send_seq & 0x07)), | ||
| 650 | dev->sh_mem + BANK4); | ||
| 651 | dev->w_busy = dev->r_busy = 0; | ||
| 652 | |||
| 653 | } | ||
| 654 | |||
| 655 | static void | ||
| 656 | pcbit_l2_error(struct pcbit_dev *dev) | ||
| 657 | { | ||
| 658 | if (dev->l2_state == L2_RUNNING) { | ||
| 659 | |||
| 660 | printk(KERN_INFO "pcbit: layer 2 error\n"); | ||
| 661 | |||
| 662 | #ifdef DEBUG | ||
| 663 | log_state(dev); | ||
| 664 | #endif | ||
| 665 | |||
| 666 | dev->l2_state = L2_DOWN; | ||
| 667 | |||
| 668 | init_timer(&dev->error_recover_timer); | ||
| 669 | dev->error_recover_timer.function = &pcbit_l2_err_recover; | ||
| 670 | dev->error_recover_timer.data = (ulong) dev; | ||
| 671 | dev->error_recover_timer.expires = jiffies + ERRTIME; | ||
| 672 | add_timer(&dev->error_recover_timer); | ||
| 673 | } | ||
| 674 | } | ||
| 675 | |||
| 676 | /* | ||
| 677 | * Description: | ||
| 678 | * if board acks frames | ||
| 679 | * update dev->free | ||
| 680 | * call pcbit_transmit to write possible queued frames | ||
| 681 | */ | ||
| 682 | |||
| 683 | static void | ||
| 684 | pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack) | ||
| 685 | { | ||
| 686 | int i, | ||
| 687 | count; | ||
| 688 | int unacked; | ||
| 689 | |||
| 690 | unacked = (dev->send_seq + (8 - dev->unack_seq)) & 0x07; | ||
| 691 | |||
| 692 | /* dev->unack_seq < ack <= dev->send_seq; */ | ||
| 693 | |||
| 694 | if (unacked) { | ||
| 695 | |||
| 696 | if (dev->send_seq > dev->unack_seq) { | ||
| 697 | if (ack <= dev->unack_seq || ack > dev->send_seq) { | ||
| 698 | printk(KERN_DEBUG | ||
| 699 | "layer 2 ack unacceptable - dev %d", | ||
| 700 | dev->id); | ||
| 701 | |||
| 702 | pcbit_l2_error(dev); | ||
| 703 | } else if (ack > dev->send_seq && ack <= dev->unack_seq) { | ||
| 704 | printk(KERN_DEBUG | ||
| 705 | "layer 2 ack unacceptable - dev %d", | ||
| 706 | dev->id); | ||
| 707 | pcbit_l2_error(dev); | ||
| 708 | } | ||
| 709 | } | ||
| 710 | /* ack is acceptable */ | ||
| 711 | |||
| 712 | |||
| 713 | i = dev->unack_seq; | ||
| 714 | |||
| 715 | do { | ||
| 716 | dev->unack_seq = i = (i + 1) % 8; | ||
| 717 | dev->free += dev->fsize[i]; | ||
| 718 | } while (i != ack); | ||
| 719 | |||
| 720 | count = 0; | ||
| 721 | while (count < 7 && dev->write_queue) { | ||
| 722 | u8 lsend_seq = dev->send_seq; | ||
| 723 | |||
| 724 | pcbit_transmit(dev); | ||
| 725 | |||
| 726 | if (dev->send_seq == lsend_seq) | ||
| 727 | break; | ||
| 728 | count++; | ||
| 729 | } | ||
| 730 | } else | ||
| 731 | printk(KERN_DEBUG "recv_ack: unacked = 0\n"); | ||
| 732 | } | ||
diff --git a/drivers/isdn/pcbit/layer2.h b/drivers/isdn/pcbit/layer2.h new file mode 100644 index 000000000000..0d99da3a3e2b --- /dev/null +++ b/drivers/isdn/pcbit/layer2.h | |||
| @@ -0,0 +1,288 @@ | |||
| 1 | /* | ||
| 2 | * PCBIT-D low-layer interface definitions | ||
| 3 | * | ||
| 4 | * Copyright (C) 1996 Universidade de Lisboa | ||
| 5 | * | ||
| 6 | * Written by Pedro Roque Marques (roque@di.fc.ul.pt) | ||
| 7 | * | ||
| 8 | * This software may be used and distributed according to the terms of | ||
| 9 | * the GNU General Public License, incorporated herein by reference. | ||
| 10 | */ | ||
| 11 | |||
| 12 | /* | ||
| 13 | * 19991203 - Fernando Carvalho - takion@superbofh.org | ||
| 14 | * Hacked to compile with egcs and run with current version of isdn modules | ||
| 15 | */ | ||
| 16 | |||
| 17 | #ifndef LAYER2_H | ||
| 18 | #define LAYER2_H | ||
| 19 | |||
| 20 | #include <linux/interrupt.h> | ||
| 21 | |||
| 22 | #include <asm/byteorder.h> | ||
| 23 | |||
| 24 | #define BANK1 0x0000U /* PC -> Board */ | ||
| 25 | #define BANK2 0x01ffU /* Board -> PC */ | ||
| 26 | #define BANK3 0x03feU /* Att Board */ | ||
| 27 | #define BANK4 0x03ffU /* Att PC */ | ||
| 28 | |||
| 29 | #define BANKLEN 0x01FFU | ||
| 30 | |||
| 31 | #define LOAD_ZONE_START 0x03f8U | ||
| 32 | #define LOAD_ZONE_END 0x03fdU | ||
| 33 | |||
| 34 | #define LOAD_RETRY 18000000 | ||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | /* TAM - XX - C - S - NUM */ | ||
| 39 | #define PREHDR_LEN 8 | ||
| 40 | /* TT - M - I - TH - TD */ | ||
| 41 | #define FRAME_HDR_LEN 8 | ||
| 42 | |||
| 43 | #define MSG_CONN_REQ 0x08000100 | ||
| 44 | #define MSG_CONN_CONF 0x00000101 | ||
| 45 | #define MSG_CONN_IND 0x00000102 | ||
| 46 | #define MSG_CONN_RESP 0x08000103 | ||
| 47 | |||
| 48 | #define MSG_CONN_ACTV_REQ 0x08000300 | ||
| 49 | #define MSG_CONN_ACTV_CONF 0x00000301 | ||
| 50 | #define MSG_CONN_ACTV_IND 0x00000302 | ||
| 51 | #define MSG_CONN_ACTV_RESP 0x08000303 | ||
| 52 | |||
| 53 | #define MSG_DISC_REQ 0x08000400 | ||
| 54 | #define MSG_DISC_CONF 0x00000401 | ||
| 55 | #define MSG_DISC_IND 0x00000402 | ||
| 56 | #define MSG_DISC_RESP 0x08000403 | ||
| 57 | |||
| 58 | #define MSG_TDATA_REQ 0x0908E200 | ||
| 59 | #define MSG_TDATA_CONF 0x0000E201 | ||
| 60 | #define MSG_TDATA_IND 0x0000E202 | ||
| 61 | #define MSG_TDATA_RESP 0x0908E203 | ||
| 62 | |||
| 63 | #define MSG_SELP_REQ 0x09004000 | ||
| 64 | #define MSG_SELP_CONF 0x00004001 | ||
| 65 | |||
| 66 | #define MSG_ACT_TRANSP_REQ 0x0908E000 | ||
| 67 | #define MSG_ACT_TRANSP_CONF 0x0000E001 | ||
| 68 | |||
| 69 | #define MSG_STPROT_REQ 0x09004100 | ||
| 70 | #define MSG_STPROT_CONF 0x00004101 | ||
| 71 | |||
| 72 | #define MSG_PING188_REQ 0x09030500 | ||
| 73 | #define MSG_PING188_CONF 0x000005bc | ||
| 74 | |||
| 75 | #define MSG_WATCH188 0x09030400 | ||
| 76 | |||
| 77 | #define MSG_API_ON 0x08020102 | ||
| 78 | #define MSG_POOL_PCBIT 0x08020400 | ||
| 79 | #define MSG_POOL_PCBIT_CONF 0x00000401 | ||
| 80 | |||
| 81 | #define MSG_INFO_IND 0x00002602 | ||
| 82 | #define MSG_INFO_RESP 0x08002603 | ||
| 83 | |||
| 84 | #define MSG_DEBUG_188 0x0000ff00 | ||
| 85 | |||
| 86 | /* | ||
| 87 | |||
| 88 | long 4 3 2 1 | ||
| 89 | Intel 1 2 3 4 | ||
| 90 | */ | ||
| 91 | |||
| 92 | #ifdef __LITTLE_ENDIAN | ||
| 93 | #define SET_MSG_SCMD(msg, ch) (msg = (msg & 0xffffff00) | (((ch) & 0xff))) | ||
| 94 | #define SET_MSG_CMD(msg, ch) (msg = (msg & 0xffff00ff) | (((ch) & 0xff) << 8)) | ||
| 95 | #define SET_MSG_PROC(msg, ch) (msg = (msg & 0xff00ffff) | (((ch) & 0xff) << 16)) | ||
| 96 | #define SET_MSG_CPU(msg, ch) (msg = (msg & 0x00ffffff) | (((ch) & 0xff) << 24)) | ||
| 97 | |||
| 98 | #define GET_MSG_SCMD(msg) ((msg) & 0xFF) | ||
| 99 | #define GET_MSG_CMD(msg) ((msg) >> 8 & 0xFF) | ||
| 100 | #define GET_MSG_PROC(msg) ((msg) >> 16 & 0xFF) | ||
| 101 | #define GET_MSG_CPU(msg) ((msg) >> 24) | ||
| 102 | |||
| 103 | #else | ||
| 104 | #error "Non-Intel CPU" | ||
| 105 | #endif | ||
| 106 | |||
| 107 | #define MAX_QUEUED 7 | ||
| 108 | |||
| 109 | #define SCHED_READ 0x01 | ||
| 110 | #define SCHED_WRITE 0x02 | ||
| 111 | |||
| 112 | #define SET_RUN_TIMEOUT 2*HZ /* 2 seconds */ | ||
| 113 | |||
| 114 | struct frame_buf { | ||
| 115 | ulong msg; | ||
| 116 | unsigned int refnum; | ||
| 117 | unsigned int dt_len; | ||
| 118 | unsigned int hdr_len; | ||
| 119 | struct sk_buff *skb; | ||
| 120 | unsigned int copied; | ||
| 121 | struct frame_buf * next; | ||
| 122 | }; | ||
| 123 | |||
| 124 | extern int pcbit_l2_write(struct pcbit_dev * dev, ulong msg, ushort refnum, | ||
| 125 | struct sk_buff *skb, unsigned short hdr_len); | ||
| 126 | |||
| 127 | extern irqreturn_t pcbit_irq_handler(int interrupt, void *, struct pt_regs *regs); | ||
| 128 | |||
| 129 | extern struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS]; | ||
| 130 | |||
| 131 | #ifdef DEBUG | ||
| 132 | static __inline__ void log_state(struct pcbit_dev *dev) { | ||
| 133 | printk(KERN_DEBUG "writeptr = %ld\n", | ||
| 134 | (ulong) (dev->writeptr - dev->sh_mem)); | ||
| 135 | printk(KERN_DEBUG "readptr = %ld\n", | ||
| 136 | (ulong) (dev->readptr - (dev->sh_mem + BANK2))); | ||
| 137 | printk(KERN_DEBUG "{rcv_seq=%01x, send_seq=%01x, unack_seq=%01x}\n", | ||
| 138 | dev->rcv_seq, dev->send_seq, dev->unack_seq); | ||
| 139 | } | ||
| 140 | #endif | ||
| 141 | |||
| 142 | static __inline__ struct pcbit_dev * chan2dev(struct pcbit_chan * chan) | ||
| 143 | { | ||
| 144 | struct pcbit_dev * dev; | ||
| 145 | int i; | ||
| 146 | |||
| 147 | |||
| 148 | for (i=0; i<MAX_PCBIT_CARDS; i++) | ||
| 149 | if ((dev=dev_pcbit[i])) | ||
| 150 | if (dev->b1 == chan || dev->b2 == chan) | ||
| 151 | return dev; | ||
| 152 | return NULL; | ||
| 153 | |||
| 154 | } | ||
| 155 | |||
| 156 | static __inline__ struct pcbit_dev * finddev(int id) | ||
| 157 | { | ||
| 158 | struct pcbit_dev * dev; | ||
| 159 | int i; | ||
| 160 | |||
| 161 | for (i=0; i<MAX_PCBIT_CARDS; i++) | ||
| 162 | if ((dev=dev_pcbit[i])) | ||
| 163 | if (dev->id == id) | ||
| 164 | return dev; | ||
| 165 | return NULL; | ||
| 166 | } | ||
| 167 | |||
| 168 | |||
| 169 | /* | ||
| 170 | * Support routines for reading and writing in the board | ||
| 171 | */ | ||
| 172 | |||
| 173 | static __inline__ void pcbit_writeb(struct pcbit_dev *dev, unsigned char dt) | ||
| 174 | { | ||
| 175 | writeb(dt, dev->writeptr++); | ||
| 176 | if (dev->writeptr == dev->sh_mem + BANKLEN) | ||
| 177 | dev->writeptr = dev->sh_mem; | ||
| 178 | } | ||
| 179 | |||
| 180 | static __inline__ void pcbit_writew(struct pcbit_dev *dev, unsigned short dt) | ||
| 181 | { | ||
| 182 | int dist; | ||
| 183 | |||
| 184 | dist = BANKLEN - (dev->writeptr - dev->sh_mem); | ||
| 185 | switch (dist) { | ||
| 186 | case 2: | ||
| 187 | writew(dt, dev->writeptr); | ||
| 188 | dev->writeptr = dev->sh_mem; | ||
| 189 | break; | ||
| 190 | case 1: | ||
| 191 | writeb((u_char) (dt & 0x00ffU), dev->writeptr); | ||
| 192 | dev->writeptr = dev->sh_mem; | ||
| 193 | writeb((u_char) (dt >> 8), dev->writeptr++); | ||
| 194 | break; | ||
| 195 | default: | ||
| 196 | writew(dt, dev->writeptr); | ||
| 197 | dev->writeptr += 2; | ||
| 198 | break; | ||
| 199 | }; | ||
| 200 | } | ||
| 201 | |||
| 202 | static __inline__ void memcpy_topcbit(struct pcbit_dev * dev, u_char * data, | ||
| 203 | int len) | ||
| 204 | { | ||
| 205 | int diff; | ||
| 206 | |||
| 207 | diff = len - (BANKLEN - (dev->writeptr - dev->sh_mem) ); | ||
| 208 | |||
| 209 | if (diff > 0) | ||
| 210 | { | ||
| 211 | memcpy_toio(dev->writeptr, data, len - diff); | ||
| 212 | memcpy_toio(dev->sh_mem, data + (len - diff), diff); | ||
| 213 | dev->writeptr = dev->sh_mem + diff; | ||
| 214 | } | ||
| 215 | else | ||
| 216 | { | ||
| 217 | memcpy_toio(dev->writeptr, data, len); | ||
| 218 | |||
| 219 | dev->writeptr += len; | ||
| 220 | if (diff == 0) | ||
| 221 | dev->writeptr = dev->sh_mem; | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | static __inline__ unsigned char pcbit_readb(struct pcbit_dev *dev) | ||
| 226 | { | ||
| 227 | unsigned char val; | ||
| 228 | |||
| 229 | val = readb(dev->readptr++); | ||
| 230 | if (dev->readptr == dev->sh_mem + BANK2 + BANKLEN) | ||
| 231 | dev->readptr = dev->sh_mem + BANK2; | ||
| 232 | |||
| 233 | return val; | ||
| 234 | } | ||
| 235 | |||
| 236 | static __inline__ unsigned short pcbit_readw(struct pcbit_dev *dev) | ||
| 237 | { | ||
| 238 | int dist; | ||
| 239 | unsigned short val; | ||
| 240 | |||
| 241 | dist = BANKLEN - ( dev->readptr - (dev->sh_mem + BANK2 ) ); | ||
| 242 | switch (dist) { | ||
| 243 | case 2: | ||
| 244 | val = readw(dev->readptr); | ||
| 245 | dev->readptr = dev->sh_mem + BANK2; | ||
| 246 | break; | ||
| 247 | case 1: | ||
| 248 | val = readb(dev->readptr); | ||
| 249 | dev->readptr = dev->sh_mem + BANK2; | ||
| 250 | val = (readb(dev->readptr++) << 8) | val; | ||
| 251 | break; | ||
| 252 | default: | ||
| 253 | val = readw(dev->readptr); | ||
| 254 | dev->readptr += 2; | ||
| 255 | break; | ||
| 256 | }; | ||
| 257 | return val; | ||
| 258 | } | ||
| 259 | |||
| 260 | static __inline__ void memcpy_frompcbit(struct pcbit_dev * dev, u_char * data, int len) | ||
| 261 | { | ||
| 262 | int diff; | ||
| 263 | |||
| 264 | diff = len - (BANKLEN - (dev->readptr - (dev->sh_mem + BANK2) ) ); | ||
| 265 | if (diff > 0) | ||
| 266 | { | ||
| 267 | memcpy_fromio(data, dev->readptr, len - diff); | ||
| 268 | memcpy_fromio(data + (len - diff), dev->sh_mem + BANK2 , diff); | ||
| 269 | dev->readptr = dev->sh_mem + BANK2 + diff; | ||
| 270 | } | ||
| 271 | else | ||
| 272 | { | ||
| 273 | memcpy_fromio(data, dev->readptr, len); | ||
| 274 | dev->readptr += len; | ||
| 275 | if (diff == 0) | ||
| 276 | dev->readptr = dev->sh_mem + BANK2; | ||
| 277 | } | ||
| 278 | } | ||
| 279 | |||
| 280 | |||
| 281 | #endif | ||
| 282 | |||
| 283 | |||
| 284 | |||
| 285 | |||
| 286 | |||
| 287 | |||
| 288 | |||
diff --git a/drivers/isdn/pcbit/module.c b/drivers/isdn/pcbit/module.c new file mode 100644 index 000000000000..282073a35d6a --- /dev/null +++ b/drivers/isdn/pcbit/module.c | |||
| @@ -0,0 +1,130 @@ | |||
| 1 | /* | ||
| 2 | * PCBIT-D module support | ||
| 3 | * | ||
| 4 | * Copyright (C) 1996 Universidade de Lisboa | ||
| 5 | * | ||
| 6 | * Written by Pedro Roque Marques (roque@di.fc.ul.pt) | ||
| 7 | * | ||
| 8 | * This software may be used and distributed according to the terms of | ||
| 9 | * the GNU General Public License, incorporated herein by reference. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/init.h> | ||
| 14 | #include <linux/sched.h> | ||
| 15 | #include <linux/string.h> | ||
| 16 | #include <linux/kernel.h> | ||
| 17 | #include <linux/skbuff.h> | ||
| 18 | |||
| 19 | #include <linux/isdnif.h> | ||
| 20 | #include "pcbit.h" | ||
| 21 | |||
| 22 | MODULE_DESCRIPTION("ISDN4Linux: Driver for PCBIT-T card"); | ||
| 23 | MODULE_AUTHOR("Pedro Roque Marques"); | ||
| 24 | MODULE_LICENSE("GPL"); | ||
| 25 | |||
| 26 | static int mem[MAX_PCBIT_CARDS]; | ||
| 27 | static int irq[MAX_PCBIT_CARDS]; | ||
| 28 | |||
| 29 | module_param_array(mem, int, NULL, 0); | ||
| 30 | module_param_array(irq, int, NULL, 0); | ||
| 31 | |||
| 32 | static int num_boards; | ||
| 33 | struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS]; | ||
| 34 | |||
| 35 | extern void pcbit_terminate(int board); | ||
| 36 | extern int pcbit_init_dev(int board, int mem_base, int irq); | ||
| 37 | |||
| 38 | static int __init pcbit_init(void) | ||
| 39 | { | ||
| 40 | int board; | ||
| 41 | |||
| 42 | num_boards = 0; | ||
| 43 | |||
| 44 | printk(KERN_NOTICE | ||
| 45 | "PCBIT-D device driver v 0.5-fjpc0 19991204 - " | ||
| 46 | "Copyright (C) 1996 Universidade de Lisboa\n"); | ||
| 47 | |||
| 48 | if (mem[0] || irq[0]) | ||
| 49 | { | ||
| 50 | for (board=0; board < MAX_PCBIT_CARDS && mem[board] && irq[board]; board++) | ||
| 51 | { | ||
| 52 | if (!mem[board]) | ||
| 53 | mem[board] = 0xD0000; | ||
| 54 | if (!irq[board]) | ||
| 55 | irq[board] = 5; | ||
| 56 | |||
| 57 | if (pcbit_init_dev(board, mem[board], irq[board]) == 0) | ||
| 58 | num_boards++; | ||
| 59 | |||
| 60 | else | ||
| 61 | { | ||
| 62 | printk(KERN_WARNING | ||
| 63 | "pcbit_init failed for dev %d", | ||
| 64 | board + 1); | ||
| 65 | return -EIO; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | /* Hardcoded default settings detection */ | ||
| 71 | |||
| 72 | if (!num_boards) | ||
| 73 | { | ||
| 74 | printk(KERN_INFO | ||
| 75 | "Trying to detect board using default settings\n"); | ||
| 76 | if (pcbit_init_dev(0, 0xD0000, 5) == 0) | ||
| 77 | num_boards++; | ||
| 78 | else | ||
| 79 | return -EIO; | ||
| 80 | } | ||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | static void __exit pcbit_exit(void) | ||
| 85 | { | ||
| 86 | #ifdef MODULE | ||
| 87 | int board; | ||
| 88 | |||
| 89 | for (board = 0; board < num_boards; board++) | ||
| 90 | pcbit_terminate(board); | ||
| 91 | printk(KERN_NOTICE | ||
| 92 | "PCBIT-D module unloaded\n"); | ||
| 93 | #endif | ||
| 94 | } | ||
| 95 | |||
| 96 | #ifndef MODULE | ||
| 97 | #define MAX_PARA (MAX_PCBIT_CARDS * 2) | ||
| 98 | static int __init pcbit_setup(char *line) | ||
| 99 | { | ||
| 100 | int i, j, argc; | ||
| 101 | char *str; | ||
| 102 | int ints[MAX_PARA+1]; | ||
| 103 | |||
| 104 | str = get_options(line, MAX_PARA, ints); | ||
| 105 | argc = ints[0]; | ||
| 106 | i = 0; | ||
| 107 | j = 1; | ||
| 108 | |||
| 109 | while (argc && (i<MAX_PCBIT_CARDS)) { | ||
| 110 | |||
| 111 | if (argc) { | ||
| 112 | mem[i] = ints[j]; | ||
| 113 | j++; argc--; | ||
| 114 | } | ||
| 115 | |||
| 116 | if (argc) { | ||
| 117 | irq[i] = ints[j]; | ||
| 118 | j++; argc--; | ||
| 119 | } | ||
| 120 | |||
| 121 | i++; | ||
| 122 | } | ||
| 123 | return(1); | ||
| 124 | } | ||
| 125 | __setup("pcbit=", pcbit_setup); | ||
| 126 | #endif | ||
| 127 | |||
| 128 | module_init(pcbit_init); | ||
| 129 | module_exit(pcbit_exit); | ||
| 130 | |||
diff --git a/drivers/isdn/pcbit/pcbit.h b/drivers/isdn/pcbit/pcbit.h new file mode 100644 index 000000000000..388bacefd23a --- /dev/null +++ b/drivers/isdn/pcbit/pcbit.h | |||
| @@ -0,0 +1,169 @@ | |||
| 1 | /* | ||
| 2 | * PCBIT-D device driver definitions | ||
| 3 | * | ||
| 4 | * Copyright (C) 1996 Universidade de Lisboa | ||
| 5 | * | ||
| 6 | * Written by Pedro Roque Marques (roque@di.fc.ul.pt) | ||
| 7 | * | ||
| 8 | * This software may be used and distributed according to the terms of | ||
| 9 | * the GNU General Public License, incorporated herein by reference. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef PCBIT_H | ||
| 13 | #define PCBIT_H | ||
| 14 | |||
| 15 | #include <linux/workqueue.h> | ||
| 16 | |||
| 17 | #define MAX_PCBIT_CARDS 4 | ||
| 18 | |||
| 19 | |||
| 20 | #define BLOCK_TIMER | ||
| 21 | |||
| 22 | #ifdef __KERNEL__ | ||
| 23 | |||
| 24 | struct pcbit_chan { | ||
| 25 | unsigned short id; | ||
| 26 | unsigned short callref; /* Call Reference */ | ||
| 27 | unsigned char proto; /* layer2protocol */ | ||
| 28 | unsigned char queued; /* unacked data messages */ | ||
| 29 | unsigned char layer2link; /* used in TData */ | ||
| 30 | unsigned char snum; /* used in TData */ | ||
| 31 | unsigned short s_refnum; | ||
| 32 | unsigned short r_refnum; | ||
| 33 | unsigned short fsm_state; | ||
| 34 | struct timer_list fsm_timer; | ||
| 35 | #ifdef BLOCK_TIMER | ||
| 36 | struct timer_list block_timer; | ||
| 37 | #endif | ||
| 38 | }; | ||
| 39 | |||
| 40 | struct msn_entry { | ||
| 41 | char *msn; | ||
| 42 | struct msn_entry * next; | ||
| 43 | }; | ||
| 44 | |||
| 45 | struct pcbit_dev { | ||
| 46 | /* board */ | ||
| 47 | |||
| 48 | volatile unsigned char __iomem *sh_mem; /* RDP address */ | ||
| 49 | unsigned long ph_mem; | ||
| 50 | unsigned int irq; | ||
| 51 | unsigned int id; | ||
| 52 | unsigned int interrupt; /* set during interrupt | ||
| 53 | processing */ | ||
| 54 | spinlock_t lock; | ||
| 55 | /* isdn4linux */ | ||
| 56 | |||
| 57 | struct msn_entry * msn_list; /* ISDN address list */ | ||
| 58 | |||
| 59 | isdn_if * dev_if; | ||
| 60 | |||
| 61 | ushort ll_hdrlen; | ||
| 62 | ushort hl_hdrlen; | ||
| 63 | |||
| 64 | /* link layer */ | ||
| 65 | unsigned char l2_state; | ||
| 66 | |||
| 67 | struct frame_buf *read_queue; | ||
| 68 | struct frame_buf *read_frame; | ||
| 69 | struct frame_buf *write_queue; | ||
| 70 | |||
| 71 | /* Protocol start */ | ||
| 72 | wait_queue_head_t set_running_wq; | ||
| 73 | struct timer_list set_running_timer; | ||
| 74 | |||
| 75 | struct timer_list error_recover_timer; | ||
| 76 | |||
| 77 | struct work_struct qdelivery; | ||
| 78 | |||
| 79 | u_char w_busy; | ||
| 80 | u_char r_busy; | ||
| 81 | |||
| 82 | volatile unsigned char __iomem *readptr; | ||
| 83 | volatile unsigned char __iomem *writeptr; | ||
| 84 | |||
| 85 | ushort loadptr; | ||
| 86 | |||
| 87 | unsigned short fsize[8]; /* sent layer2 frames size */ | ||
| 88 | |||
| 89 | unsigned char send_seq; | ||
| 90 | unsigned char rcv_seq; | ||
| 91 | unsigned char unack_seq; | ||
| 92 | |||
| 93 | unsigned short free; | ||
| 94 | |||
| 95 | /* channels */ | ||
| 96 | |||
| 97 | struct pcbit_chan *b1; | ||
| 98 | struct pcbit_chan *b2; | ||
| 99 | }; | ||
| 100 | |||
| 101 | #define STATS_TIMER (10*HZ) | ||
| 102 | #define ERRTIME (HZ/10) | ||
| 103 | |||
| 104 | /* MRU */ | ||
| 105 | #define MAXBUFSIZE 1534 | ||
| 106 | #define MRU MAXBUFSIZE | ||
| 107 | |||
| 108 | #define STATBUF_LEN 2048 | ||
| 109 | /* | ||
| 110 | * | ||
| 111 | */ | ||
| 112 | |||
| 113 | #endif /* __KERNEL__ */ | ||
| 114 | |||
| 115 | /* isdn_ctrl only allows a long sized argument */ | ||
| 116 | |||
| 117 | struct pcbit_ioctl { | ||
| 118 | union { | ||
| 119 | struct byte_op { | ||
| 120 | ushort addr; | ||
| 121 | ushort value; | ||
| 122 | } rdp_byte; | ||
| 123 | unsigned long l2_status; | ||
| 124 | } info; | ||
| 125 | }; | ||
| 126 | |||
| 127 | |||
| 128 | |||
| 129 | #define PCBIT_IOCTL_GETSTAT 0x01 /* layer2 status */ | ||
| 130 | #define PCBIT_IOCTL_LWMODE 0x02 /* linear write mode */ | ||
| 131 | #define PCBIT_IOCTL_STRLOAD 0x03 /* start load mode */ | ||
| 132 | #define PCBIT_IOCTL_ENDLOAD 0x04 /* end load mode */ | ||
| 133 | #define PCBIT_IOCTL_SETBYTE 0x05 /* set byte */ | ||
| 134 | #define PCBIT_IOCTL_GETBYTE 0x06 /* get byte */ | ||
| 135 | #define PCBIT_IOCTL_RUNNING 0x07 /* set protocol running */ | ||
| 136 | #define PCBIT_IOCTL_WATCH188 0x08 /* set watch 188 */ | ||
| 137 | #define PCBIT_IOCTL_PING188 0x09 /* ping 188 */ | ||
| 138 | #define PCBIT_IOCTL_FWMODE 0x0A /* firmware write mode */ | ||
| 139 | #define PCBIT_IOCTL_STOP 0x0B /* stop protocol */ | ||
| 140 | #define PCBIT_IOCTL_APION 0x0C /* issue API_ON */ | ||
| 141 | |||
| 142 | #ifndef __KERNEL__ | ||
| 143 | |||
| 144 | #define PCBIT_GETSTAT (PCBIT_IOCTL_GETSTAT + IIOCDRVCTL) | ||
| 145 | #define PCBIT_LWMODE (PCBIT_IOCTL_LWMODE + IIOCDRVCTL) | ||
| 146 | #define PCBIT_STRLOAD (PCBIT_IOCTL_STRLOAD + IIOCDRVCTL) | ||
| 147 | #define PCBIT_ENDLOAD (PCBIT_IOCTL_ENDLOAD + IIOCDRVCTL) | ||
| 148 | #define PCBIT_SETBYTE (PCBIT_IOCTL_SETBYTE + IIOCDRVCTL) | ||
| 149 | #define PCBIT_GETBYTE (PCBIT_IOCTL_GETBYTE + IIOCDRVCTL) | ||
| 150 | #define PCBIT_RUNNING (PCBIT_IOCTL_RUNNING + IIOCDRVCTL) | ||
| 151 | #define PCBIT_WATCH188 (PCBIT_IOCTL_WATCH188 + IIOCDRVCTL) | ||
| 152 | #define PCBIT_PING188 (PCBIT_IOCTL_PING188 + IIOCDRVCTL) | ||
| 153 | #define PCBIT_FWMODE (PCBIT_IOCTL_FWMODE + IIOCDRVCTL) | ||
| 154 | #define PCBIT_STOP (PCBIT_IOCTL_STOP + IIOCDRVCTL) | ||
| 155 | #define PCBIT_APION (PCBIT_IOCTL_APION + IIOCDRVCTL) | ||
| 156 | |||
| 157 | #define MAXSUPERLINE 3000 | ||
| 158 | |||
| 159 | #endif | ||
| 160 | |||
| 161 | #define L2_DOWN 0 | ||
| 162 | #define L2_LOADING 1 | ||
| 163 | #define L2_LWMODE 2 | ||
| 164 | #define L2_FWMODE 3 | ||
| 165 | #define L2_STARTING 4 | ||
| 166 | #define L2_RUNNING 5 | ||
| 167 | #define L2_ERROR 6 | ||
| 168 | |||
| 169 | #endif | ||
