1/* $Id: tpam_nco.c,v 1.1.1.1 2008/10/15 03:26:34 james26_jang Exp $ 2 * 3 * Turbo PAM ISDN driver for Linux. 4 * (Kernel Driver - Low Level NCO Manipulation) 5 * 6 * Copyright 2001 Stelian Pop <stelian.pop@fr.alcove.com>, Alc�ve 7 * 8 * This software may be used and distributed according to the terms 9 * of the GNU General Public License, incorporated herein by reference. 10 * 11 * For all support questions please contact: <support@auvertech.fr> 12 * 13 */ 14 15#include <linux/pci.h> 16#include <linux/sched.h> 17#include <linux/tqueue.h> 18#include <linux/interrupt.h> 19#include <asm/io.h> 20 21#include "tpam.h" 22 23/* Local function prototypes */ 24static struct sk_buff *build_NCOpacket(u16, u16, u16, u16, u16); 25static int extract_NCOParameter(struct sk_buff *, u8, void *, u16); 26 27/* 28 * Build a NCO packet (PCI message). 29 * 30 * messageID: the message type (ID_*) 31 * size: size of the TLV block 32 * data_size: size of the data block 33 * ack: packet needs to send ack upon send 34 * ack_size: size of data to be acknowledged upon send 35 * 36 * Return: the sk_buff filled with the NCO packet, or NULL if error. 37 */ 38static struct sk_buff *build_NCOpacket(u16 messageID, u16 size, 39 u16 data_size, u16 ack, 40 u16 ack_size) { 41 struct sk_buff *skb; 42 skb_header *h; 43 pci_mpb *p; 44 u16 finalsize; 45 46 /* reserve enough space for the sk_buff header, the pci * header, 47 * size bytes for the TLV block, size bytes for the data and 4 more 48 * bytes in order to make sure we can write dwords to the board. */ 49 finalsize = sizeof(skb_header) + sizeof(pci_mpb) + size + data_size + 4; 50 51 /* allocate the sk_buff */ 52 if (!(skb = alloc_skb(finalsize, GFP_ATOMIC))) { 53 printk(KERN_ERR "TurboPAM(make_NCOpacket): alloc_skb failed\n"); 54 return NULL; 55 } 56 57 /* construct the skb_header */ 58 h = (skb_header *)skb_put(skb, sizeof(skb_header)); 59 h->size = sizeof(pci_mpb) + size; 60 h->data_size = data_size; 61 h->ack = ack; 62 h->ack_size = ack_size; 63 64 /* construct the pci_mpb */ 65 p = (pci_mpb *)skb_put(skb, sizeof(pci_mpb)); 66 p->exID = 0; 67 p->flags = 0; 68 p->errorCode = 0; 69 p->messageID = messageID; 70 p->maximumBlockTLVSize = MPB_MAXIMUMBLOCKTLVSIZE; 71 p->actualBlockTLVSize = size; 72 p->maximumDataSize = MPB_MAXIMUMDATASIZE; 73 p->actualDataSize = data_size; 74 return skb; 75} 76 77/* 78 * Build a ACreateNCOReq message. 79 * 80 * phone: the local phone number. 81 * 82 * Return: the sk_buff filled with the NCO packet, or NULL if error. 83 */ 84struct sk_buff *build_ACreateNCOReq(const u8 *phone) { 85 struct sk_buff *skb; 86 u8 *tlv; 87 88 dprintk("TurboPAM(build_ACreateNCOReq): phone=%s\n", phone); 89 90 /* build the NCO packet */ 91 if (!(skb = build_NCOpacket(ID_ACreateNCOReq, 23 + strlen(phone), 0, 0, 0))) 92 return NULL; 93 94 /* add the parameters */ 95 tlv = (u8 *)skb_put(skb, 3); 96 *tlv = PAR_NCOType; 97 *(tlv+1) = 1; 98 *(tlv+2) = 5; /* mistery value... */ 99 100 tlv = (u8 *)skb_put(skb, 4); 101 *tlv = PAR_U3Protocol; 102 *(tlv+1) = 2; 103 *(tlv+2) = 4; /* no level 3 protocol */ 104 *(tlv+3) = 1; /* HDLC in level 2 */ 105 106 tlv = (u8 *)skb_put(skb, 3); 107 *tlv = PAR_Cdirection; 108 *(tlv+1) = 1; 109 *(tlv+2) = 3; /* PCI_DIRECTION_BOTH */ 110 111 tlv = (u8 *)skb_put(skb, 3); 112 *tlv = PAR_Udirection; 113 *(tlv+1) = 1; 114 *(tlv+2) = 3; /* PCI_DIRECTION_BOTH */ 115 116 tlv = (u8 *)skb_put(skb, 4); 117 *tlv = PAR_BearerCap; 118 *(tlv+1) = 2; 119 *(tlv+2) = 0x88; 120 *(tlv+3) = 0x90; 121 122 tlv = (u8 *)skb_put(skb, 6 + strlen(phone)); 123 *tlv = PAR_CallingNumber; 124 *(tlv+1) = strlen(phone) + 4; 125 *(tlv+2) = 0x01; /* international */ 126 *(tlv+3) = 0x01; /* isdn */ 127 *(tlv+4) = 0x00; 128 *(tlv+5) = 0x00; 129 memcpy(tlv + 6, phone, strlen(phone)); 130 131 return skb; 132} 133 134/* 135 * Build a ADestroyNCOReq message. 136 * 137 * ncoid: the NCO id. 138 * 139 * Return: the sk_buff filled with the NCO packet, or NULL if error. 140 */ 141struct sk_buff *build_ADestroyNCOReq(u32 ncoid) { 142 struct sk_buff *skb; 143 u8 *tlv; 144 145 dprintk("TurboPAM(build_ADestroyNCOReq): ncoid=%lu\n", 146 (unsigned long)ncoid); 147 148 /* build the NCO packet */ 149 if (!(skb = build_NCOpacket(ID_ADestroyNCOReq, 6, 0, 0, 0))) 150 return NULL; 151 152 /* add the parameters */ 153 tlv = (u8 *)skb_put(skb, 6); 154 *tlv = PAR_NCOID; 155 *(tlv+1) = 4; 156 *((u32 *)(tlv+2)) = ncoid; 157 158 return skb; 159} 160 161/* 162 * Build a CConnectReq message. 163 * 164 * ncoid: the NCO id. 165 * called: the destination phone number 166 * hdlc: type of connection: 1 (HDLC) or 0(modem) 167 * 168 * Return: the sk_buff filled with the NCO packet, or NULL if error. 169 */ 170struct sk_buff *build_CConnectReq(u32 ncoid, const u8 *called, u8 hdlc) { 171 struct sk_buff *skb; 172 u8 *tlv; 173 174 dprintk("TurboPAM(build_CConnectReq): ncoid=%lu, called=%s, hdlc=%d\n", 175 (unsigned long)ncoid, called, hdlc); 176 177 /* build the NCO packet */ 178 if (!(skb = build_NCOpacket(ID_CConnectReq, 20 + strlen(called), 0, 0, 0))) 179 return NULL; 180 181 /* add the parameters */ 182 tlv = (u8 *)skb_put(skb, 6); 183 *tlv = PAR_NCOID; 184 *(tlv+1) = 4; 185 *((u32 *)(tlv+2)) = ncoid; 186 187 tlv = (u8 *)skb_put(skb, 4 + strlen(called)); 188 *tlv = PAR_CalledNumber; 189 *(tlv+1) = strlen(called) + 2; 190 *(tlv+2) = 0x01; /* international */ 191 *(tlv+3) = 0x01; /* isdn */ 192 memcpy(tlv + 4, called, strlen(called)); 193 194 tlv = (u8 *)skb_put(skb, 3); 195 *tlv = PAR_BearerCap; 196 *(tlv+1) = 1; 197 *(tlv+2) = hdlc ? 0x88 /* HDLC */ : 0x80 /* MODEM */; 198 199 tlv = (u8 *)skb_put(skb, 4); 200 *tlv = PAR_HLC; 201 *(tlv+1) = 2; 202 *(tlv+2) = 0x2; 203 *(tlv+3) = 0x7f; 204 205 tlv = (u8 *)skb_put(skb, 3); 206 *tlv = PAR_Facility; 207 *(tlv+1) = 1; 208 *(tlv+2) = 2; 209 210 return skb; 211} 212 213/* 214 * Build a CConnectRsp message. 215 * 216 * ncoid: the NCO id. 217 * 218 * Return: the sk_buff filled with the NCO packet, or NULL if error. 219 */ 220struct sk_buff *build_CConnectRsp(u32 ncoid) { 221 struct sk_buff *skb; 222 u8 *tlv; 223 224 dprintk("TurboPAM(build_CConnectRsp): ncoid=%lu\n", 225 (unsigned long)ncoid); 226 227 /* build the NCO packet */ 228 if (!(skb = build_NCOpacket(ID_CConnectRsp, 6, 0, 0, 0))) 229 return NULL; 230 231 /* add the parameters */ 232 tlv = (u8 *)skb_put(skb, 6); 233 *tlv = PAR_NCOID; 234 *(tlv+1) = 4; 235 *((u32 *)(tlv+2)) = ncoid; 236 237 return skb; 238} 239 240/* 241 * Build a CDisconnectReq message. 242 * 243 * ncoid: the NCO id. 244 * 245 * Return: the sk_buff filled with the NCO packet, or NULL if error. 246 */ 247struct sk_buff *build_CDisconnectReq(u32 ncoid) { 248 struct sk_buff *skb; 249 u8 *tlv; 250 251 dprintk("TurboPAM(build_CDisconnectReq): ncoid=%lu\n", 252 (unsigned long)ncoid); 253 254 /* build the NCO packet */ 255 if (!(skb = build_NCOpacket(ID_CDisconnectReq, 6, 0, 0, 0))) 256 return NULL; 257 258 /* add the parameters */ 259 tlv = (u8 *)skb_put(skb, 6); 260 *tlv = PAR_NCOID; 261 *(tlv+1) = 4; 262 *((u32 *)(tlv+2)) = ncoid; 263 264 return skb; 265} 266 267/* 268 * Build a CDisconnectRsp message. 269 * 270 * ncoid: the NCO id. 271 * 272 * Return: the sk_buff filled with the NCO packet, or NULL if error. 273 */ 274struct sk_buff *build_CDisconnectRsp(u32 ncoid) { 275 struct sk_buff *skb; 276 u8 *tlv; 277 278 dprintk("TurboPAM(build_CDisconnectRsp): ncoid=%lu\n", 279 (unsigned long)ncoid); 280 281 /* build the NCO packet */ 282 if (!(skb = build_NCOpacket(ID_CDisconnectRsp, 6, 0, 0, 0))) 283 return NULL; 284 285 /* add the parameters */ 286 tlv = (u8 *)skb_put(skb, 6); 287 *tlv = PAR_NCOID; 288 *(tlv+1) = 4; 289 *((u32 *)(tlv+2)) = ncoid; 290 291 return skb; 292} 293 294/* 295 * Build a U3DataReq message. 296 * 297 * ncoid: the NCO id. 298 * data: the data to be send 299 * len: length of the data 300 * ack: send ack upon send 301 * ack_size: size of data to be acknowledged upon send 302 * 303 * Return: the sk_buff filled with the NCO packet, or NULL if error. 304 */ 305struct sk_buff *build_U3DataReq(u32 ncoid, void *data, u16 len, 306 u16 ack, u16 ack_size) { 307 struct sk_buff *skb; 308 u8 *tlv; 309 void *p; 310 311 dprintk("TurboPAM(build_U3DataReq): " 312 "ncoid=%lu, len=%d, ack=%d, ack_size=%d\n", 313 (unsigned long)ncoid, len, ack, ack_size); 314 315 /* build the NCO packet */ 316 if (!(skb = build_NCOpacket(ID_U3DataReq, 6, len, ack, ack_size))) 317 return NULL; 318 319 /* add the parameters */ 320 tlv = (u8 *)skb_put(skb, 6); 321 *tlv = PAR_NCOID; 322 *(tlv+1) = 4; 323 *((u32 *)(tlv+2)) = ncoid; 324 325 p = skb_put(skb, len); 326 memcpy(p, data, len); 327 328 return skb; 329} 330 331/* 332 * Extract a parameter from a TLV block. 333 * 334 * skb: sk_buff containing the PCI message 335 * type: parameter to search for (PARAM_*) 336 * value: to be filled with the value of the parameter 337 * len: maximum length of the parameter value 338 * 339 * Return: 0 if OK, <0 if error. 340 */ 341static int extract_NCOParameter(struct sk_buff *skb, u8 type, 342 void *value, u16 len) { 343 void *buffer = (void *)skb->data; 344 pci_mpb *p; 345 void * bufferend; 346 u8 valtype; 347 u16 vallen; 348 349 /* calculate the start and end of the TLV block */ 350 buffer += sizeof(skb_header); 351 p = (pci_mpb *)buffer; 352 buffer += sizeof(pci_mpb); 353 bufferend = buffer + p->actualBlockTLVSize; 354 355 /* walk through the parameters */ 356 while (buffer < bufferend) { 357 358 /* parameter type */ 359 valtype = *((u8 *)buffer++); 360 /* parameter length */ 361 vallen = *((u8 *)buffer++); 362 if (vallen == 0xff) { 363 /* parameter length is on 2 bytes */ 364 vallen = *((u8 *)buffer++); 365 vallen <<= 8; 366 vallen |= *((u8 *)buffer++); 367 } 368 /* got the right parameter */ 369 if (valtype == type) { 370 /* not enough space for returning the value */ 371 if (vallen > len) 372 return -1; 373 /* OK, return it */ 374 memcpy(value, buffer, vallen); 375 return 0; 376 } 377 buffer += vallen; 378 } 379 return -1; 380} 381 382/* 383 * Parse a ACreateNCOCnf message. 384 * 385 * skb: the sk_buff containing the message 386 * status: to be filled with the status field value 387 * ncoid: to be filled with the ncoid field value 388 * 389 * Return: 0 if OK, <0 if error. 390 */ 391int parse_ACreateNCOCnf(struct sk_buff *skb, u8 *status, u32 *ncoid) { 392 393 /* extract the status */ 394 if (extract_NCOParameter(skb, PAR_CompletionStatus, status, 1)) { 395 printk(KERN_ERR "TurboPAM(parse_ACreateNCOCnf): " 396 "CompletionStatus not found\n"); 397 return -1; 398 } 399 400 if (*status) { 401 dprintk("TurboPAM(parse_ACreateNCOCnf): status=%d\n", *status); 402 return 0; 403 } 404 405 /* extract the ncoid */ 406 if (extract_NCOParameter(skb, PAR_NCOID, ncoid, 4)) { 407 printk(KERN_ERR "TurboPAM(parse_ACreateNCOCnf): " 408 "NCOID not found\n"); 409 return -1; 410 } 411 412 dprintk("TurboPAM(parse_ACreateNCOCnf): ncoid=%lu, status=%d\n", 413 (unsigned long)*ncoid, *status); 414 return 0; 415} 416 417/* 418 * Parse a ADestroyNCOCnf message. Not used in the driver. 419 * 420 * skb: the sk_buff containing the message 421 * status: to be filled with the status field value 422 * ncoid: to be filled with the ncoid field value 423 * 424 * Return: 0 if OK, <0 if error. 425 */ 426int parse_ADestroyNCOCnf(struct sk_buff *skb, u8 *status, u32 *ncoid) { 427 428 /* extract the status */ 429 if (extract_NCOParameter(skb, PAR_CompletionStatus, status, 1)) { 430 printk(KERN_ERR "TurboPAM(parse_ADestroyNCOCnf): " 431 "CompletionStatus not found\n"); 432 return -1; 433 } 434 435 if (*status) { 436 dprintk("TurboPAM(parse_ADestroyNCOCnf): status=%d\n", *status); 437 return 0; 438 } 439 440 /* extract the ncoid */ 441 if (extract_NCOParameter(skb, PAR_NCOID, ncoid, 4)) { 442 printk(KERN_ERR "TurboPAM(parse_ADestroyNCOCnf): " 443 "NCOID not found\n"); 444 return -1; 445 } 446 447 dprintk("TurboPAM(parse_ADestroyNCOCnf): ncoid=%lu, status=%d\n", 448 (unsigned long)*ncoid, *status); 449 return 0; 450} 451 452/* 453 * Parse a CConnectCnf message. 454 * 455 * skb: the sk_buff containing the message 456 * ncoid: to be filled with the ncoid field value 457 * 458 * Return: 0 if OK, <0 if error. 459 */ 460int parse_CConnectCnf(struct sk_buff *skb, u32 *ncoid) { 461 462 /* extract the ncoid */ 463 if (extract_NCOParameter(skb, PAR_NCOID, ncoid, 4)) { 464 printk(KERN_ERR "TurboPAM(parse_CConnectCnf): " 465 "NCOID not found\n"); 466 return -1; 467 } 468 dprintk("TurboPAM(parse_CConnectCnf): ncoid=%lu\n", 469 (unsigned long)*ncoid); 470 return 0; 471} 472 473/* 474 * Parse a CConnectInd message. 475 * 476 * skb: the sk_buff containing the message 477 * ncoid: to be filled with the ncoid field value 478 * hdlc: to be filled with 1 if the incoming connection is a HDLC one, 479 * with 0 if the incoming connection is a modem one 480 * calling: to be filled with the calling phone number value 481 * called: to be filled with the called phone number value 482 * plan: to be filled with the plan value 483 * screen: to be filled with the screen value 484 * 485 * Return: 0 if OK, <0 if error. 486 */ 487int parse_CConnectInd(struct sk_buff *skb, u32 *ncoid, u8 *hdlc, 488 u8 *calling, u8 *called, u8 *plan, u8 *screen) { 489 u8 phone[PHONE_MAXIMUMSIZE + 4]; 490 491 /* extract the ncoid */ 492 if (extract_NCOParameter(skb, PAR_NCOID, ncoid, 4)) { 493 printk(KERN_ERR "TurboPAM(parse_CConnectInd): " 494 "NCOID not found\n"); 495 return -1; 496 } 497 498 /* extract the bearer capability field */ 499 if (extract_NCOParameter(skb, PAR_BearerCap, hdlc, 1)) { 500 printk(KERN_ERR "TurboPAM(parse_CConnectInd): " 501 "BearerCap not found\n"); 502 return -1; 503 } 504 *hdlc = (*hdlc == 0x88) ? 1 : 0; 505 506 /* extract the calling number / plan / screen */ 507 if (extract_NCOParameter(skb, PAR_CallingNumber, phone, 508 PHONE_MAXIMUMSIZE + 4)) { 509 printk(KERN_ERR "TurboPAM(parse_CConnectInd): " 510 "CallingNumber not found\n"); 511 return -1; 512 } 513 memcpy(calling, phone + 4, PHONE_MAXIMUMSIZE); 514 *plan = phone[1]; 515 *screen = phone[3]; 516 517 /* extract the called number */ 518 if (extract_NCOParameter(skb, PAR_CalledNumber, phone, 519 PHONE_MAXIMUMSIZE + 2)) { 520 printk(KERN_ERR "TurboPAM(parse_CConnectInd): " 521 "CalledNumber not found\n"); 522 return -1; 523 } 524 memcpy(called, phone + 2, PHONE_MAXIMUMSIZE); 525 526 dprintk("TurboPAM(parse_CConnectInd): " 527 "ncoid=%lu, hdlc=%d, plan=%d, scr=%d, calling=%s, called=%s\n", 528 (unsigned long)*ncoid, *hdlc, *plan, *screen, calling, called); 529 return 0; 530} 531 532/* 533 * Parse a CDisconnectCnf message. 534 * 535 * skb: the sk_buff containing the message 536 * ncoid: to be filled with the ncoid field value 537 * causetopuf: to be filled with the cause field value 538 * 539 * Return: 0 if OK, <0 if error. 540 */ 541int parse_CDisconnectCnf(struct sk_buff *skb, u32 *ncoid, u32 *causetopuf) { 542 543 /* extract the ncoid */ 544 if (extract_NCOParameter(skb, PAR_NCOID, ncoid, 4)) { 545 printk(KERN_ERR "TurboPAM(parse_CDisconnectCnf): " 546 "NCOID not found\n"); 547 return -1; 548 } 549 550 /* extract the cause of disconnection */ 551 if (extract_NCOParameter(skb, PAR_CauseToPUF, causetopuf, 4)) { 552 printk(KERN_ERR "TurboPAM(parse_CDisconnectCnf): " 553 "CauseToPUF not found\n"); 554 return -1; 555 } 556 557 dprintk("TurboPAM(parse_CDisconnectCnf): ncoid=%lu, causetopuf=%lu\n", 558 (unsigned long)*ncoid, (unsigned long)*causetopuf); 559 return 0; 560} 561 562/* 563 * Parse a CDisconnectInd message. 564 * 565 * skb: the sk_buff containing the message 566 * ncoid: to be filled with the ncoid field value 567 * causetopuf: to be filled with the cause field value 568 * 569 * Return: 0 if OK, <0 if error. 570 */ 571int parse_CDisconnectInd(struct sk_buff *skb, u32 *ncoid, u32 *causetopuf) { 572 573 /* extract the ncoid */ 574 if (extract_NCOParameter(skb, PAR_NCOID, ncoid, 4)) { 575 printk(KERN_ERR "TurboPAM(parse_CDisconnectInd): " 576 "NCOID not found\n"); 577 return -1; 578 } 579 580 /* extract the cause of disconnection */ 581 if (extract_NCOParameter(skb, PAR_CauseToPUF, causetopuf, 4)) { 582 printk(KERN_ERR "TurboPAM(parse_CDisconnectInd): " 583 "CauseToPUF not found\n"); 584 return -1; 585 } 586 587 dprintk("TurboPAM(parse_CDisconnectInd): ncoid=%lu, causetopuf=%lu\n", 588 (unsigned long)*ncoid, (unsigned long)*causetopuf); 589 return 0; 590} 591 592/* 593 * Parse a U3ReadyToReceiveInd message. 594 * 595 * skb: the sk_buff containing the message 596 * ncoid: to be filled with the ncoid field value 597 * ready: to be filled with the ready field value 598 * 599 * Return: 0 if OK, <0 if error. 600 */ 601int parse_U3ReadyToReceiveInd(struct sk_buff *skb, u32 *ncoid, u8 *ready) { 602 603 /* extract the ncoid */ 604 if (extract_NCOParameter(skb, PAR_NCOID, ncoid, 4)) { 605 printk(KERN_ERR "TurboPAM(parse_U3ReadyToReceiveInd): " 606 "NCOID not found\n"); 607 return -1; 608 } 609 610 /* extract the ready flag */ 611 if (extract_NCOParameter(skb, PAR_ReadyFlag, ready, 1)) { 612 printk(KERN_ERR "TurboPAM(parse_U3ReadyToReceiveInd): " 613 "ReadyFlag not found\n"); 614 return -1; 615 } 616 617 dprintk("TurboPAM(parse_U3ReadyToReceiveInd): ncoid=%lu, ready=%d\n", 618 (unsigned long)*ncoid, *ready); 619 return 0; 620} 621 622/* 623 * Parse a U3DataInd message. 624 * 625 * skb: the sk_buff containing the message + data 626 * ncoid: to be filled with the ncoid field value 627 * data: to be filled with the data 628 * ready: to be filled with the data length 629 * 630 * Return: 0 if OK, <0 if error. 631 */ 632int parse_U3DataInd(struct sk_buff *skb, u32 *ncoid, u8 **data, u16 *len) { 633 pci_mpb *p; 634 635 /* extract the ncoid */ 636 if (extract_NCOParameter(skb, PAR_NCOID, ncoid, 4) == -1) { 637 printk(KERN_ERR "TurboPAM(parse_U3DataInd): NCOID not found\n"); 638 return -1; 639 } 640 641 /* get a pointer to the beginning of the data block and its length */ 642 p = (pci_mpb *)(skb->data + sizeof(skb_header)); 643 *len = p->actualDataSize; 644 skb_pull(skb, 645 sizeof(skb_header) + sizeof(pci_mpb) + p->actualBlockTLVSize); 646 *data = skb->data; 647 648 dprintk("TurboPAM(parse_U3DataInd): ncoid=%lu, datalen=%d\n", 649 (unsigned long)*ncoid, *len); 650 return 0; 651} 652 653