1/* $Id: b1.c,v 1.1.1.1 2008/10/15 03:26:33 james26_jang Exp $ 2 * 3 * Common module for AVM B1 cards. 4 * 5 * Copyright 1999 by Carsten Paeth <calle@calle.de> 6 * 7 * This software may be used and distributed according to the terms 8 * of the GNU General Public License, incorporated herein by reference. 9 * 10 */ 11 12#include <linux/module.h> 13#include <linux/kernel.h> 14#include <linux/skbuff.h> 15#include <linux/delay.h> 16#include <linux/mm.h> 17#include <linux/interrupt.h> 18#include <linux/ioport.h> 19#include <linux/capi.h> 20#include <linux/kernelcapi.h> 21#include <asm/io.h> 22#include <linux/init.h> 23#include <asm/uaccess.h> 24#include <linux/netdevice.h> 25#include "capilli.h" 26#include "avmcard.h" 27#include "capicmd.h" 28#include "capiutil.h" 29 30static char *revision = "$Revision: 1.1.1.1 $"; 31 32/* ------------------------------------------------------------- */ 33 34MODULE_DESCRIPTION("CAPI4Linux: Common support for active AVM cards"); 35MODULE_AUTHOR("Carsten Paeth"); 36MODULE_LICENSE("GPL"); 37 38/* ------------------------------------------------------------- */ 39 40int b1_irq_table[16] = 41{0, 42 0, 43 0, 44 192, /* irq 3 */ 45 32, /* irq 4 */ 46 160, /* irq 5 */ 47 96, /* irq 6 */ 48 224, /* irq 7 */ 49 0, 50 64, /* irq 9 */ 51 80, /* irq 10 */ 52 208, /* irq 11 */ 53 48, /* irq 12 */ 54 0, 55 0, 56 112, /* irq 15 */ 57}; 58 59/* ------------------------------------------------------------- */ 60 61int b1_detect(unsigned int base, enum avmcardtype cardtype) 62{ 63 int onoff, i; 64 65 /* 66 * Statusregister 0000 00xx 67 */ 68 if ((inb(base + B1_INSTAT) & 0xfc) 69 || (inb(base + B1_OUTSTAT) & 0xfc)) 70 return 1; 71 /* 72 * Statusregister 0000 001x 73 */ 74 b1outp(base, B1_INSTAT, 0x2); /* enable irq */ 75 /* b1outp(base, B1_OUTSTAT, 0x2); */ 76 if ((inb(base + B1_INSTAT) & 0xfe) != 0x2 77 /* || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2 */) 78 return 2; 79 /* 80 * Statusregister 0000 000x 81 */ 82 b1outp(base, B1_INSTAT, 0x0); /* disable irq */ 83 b1outp(base, B1_OUTSTAT, 0x0); 84 if ((inb(base + B1_INSTAT) & 0xfe) 85 || (inb(base + B1_OUTSTAT) & 0xfe)) 86 return 3; 87 88 for (onoff = !0, i= 0; i < 10 ; i++) { 89 b1_set_test_bit(base, cardtype, onoff); 90 if (b1_get_test_bit(base, cardtype) != onoff) 91 return 4; 92 onoff = !onoff; 93 } 94 95 if (cardtype == avm_m1) 96 return 0; 97 98 if ((b1_rd_reg(base, B1_STAT1(cardtype)) & 0x0f) != 0x01) 99 return 5; 100 101 return 0; 102} 103 104void b1_getrevision(avmcard *card) 105{ 106 card->class = inb(card->port + B1_ANALYSE); 107 card->revision = inb(card->port + B1_REVISION); 108} 109 110int b1_load_t4file(avmcard *card, capiloaddatapart * t4file) 111{ 112 unsigned char buf[256]; 113 unsigned char *dp; 114 int i, left, retval; 115 unsigned int base = card->port; 116 117 dp = t4file->data; 118 left = t4file->len; 119 while (left > sizeof(buf)) { 120 if (t4file->user) { 121 retval = copy_from_user(buf, dp, sizeof(buf)); 122 if (retval) 123 return -EFAULT; 124 } else { 125 memcpy(buf, dp, sizeof(buf)); 126 } 127 for (i = 0; i < sizeof(buf); i++) 128 if (b1_save_put_byte(base, buf[i]) < 0) { 129 printk(KERN_ERR "%s: corrupted firmware file ?\n", 130 card->name); 131 return -EIO; 132 } 133 left -= sizeof(buf); 134 dp += sizeof(buf); 135 } 136 if (left) { 137 if (t4file->user) { 138 retval = copy_from_user(buf, dp, left); 139 if (retval) 140 return -EFAULT; 141 } else { 142 memcpy(buf, dp, left); 143 } 144 for (i = 0; i < left; i++) 145 if (b1_save_put_byte(base, buf[i]) < 0) { 146 printk(KERN_ERR "%s: corrupted firmware file ?\n", 147 card->name); 148 return -EIO; 149 } 150 } 151 return 0; 152} 153 154int b1_load_config(avmcard *card, capiloaddatapart * config) 155{ 156 unsigned char buf[256]; 157 unsigned char *dp; 158 unsigned int base = card->port; 159 int i, j, left, retval; 160 161 dp = config->data; 162 left = config->len; 163 if (left) { 164 b1_put_byte(base, SEND_CONFIG); 165 b1_put_word(base, 1); 166 b1_put_byte(base, SEND_CONFIG); 167 b1_put_word(base, left); 168 } 169 while (left > sizeof(buf)) { 170 if (config->user) { 171 retval = copy_from_user(buf, dp, sizeof(buf)); 172 if (retval) 173 return -EFAULT; 174 } else { 175 memcpy(buf, dp, sizeof(buf)); 176 } 177 for (i = 0; i < sizeof(buf); ) { 178 b1_put_byte(base, SEND_CONFIG); 179 for (j=0; j < 4; j++) { 180 b1_put_byte(base, buf[i++]); 181 } 182 } 183 left -= sizeof(buf); 184 dp += sizeof(buf); 185 } 186 if (left) { 187 if (config->user) { 188 retval = copy_from_user(buf, dp, left); 189 if (retval) 190 return -EFAULT; 191 } else { 192 memcpy(buf, dp, left); 193 } 194 for (i = 0; i < left; ) { 195 b1_put_byte(base, SEND_CONFIG); 196 for (j=0; j < 4; j++) { 197 if (i < left) 198 b1_put_byte(base, buf[i++]); 199 else 200 b1_put_byte(base, 0); 201 } 202 } 203 } 204 return 0; 205} 206 207int b1_loaded(avmcard *card) 208{ 209 unsigned int base = card->port; 210 unsigned long stop; 211 unsigned char ans; 212 unsigned long tout = 2; 213 214 for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { 215 if (b1_tx_empty(base)) 216 break; 217 } 218 if (!b1_tx_empty(base)) { 219 printk(KERN_ERR "%s: b1_loaded: tx err, corrupted t4 file ?\n", 220 card->name); 221 return 0; 222 } 223 b1_put_byte(base, SEND_POLL); 224 for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { 225 if (b1_rx_full(base)) { 226 if ((ans = b1_get_byte(base)) == RECEIVE_POLL) { 227 return 1; 228 } 229 printk(KERN_ERR "%s: b1_loaded: got 0x%x, firmware not running\n", 230 card->name, ans); 231 return 0; 232 } 233 } 234 printk(KERN_ERR "%s: b1_loaded: firmware not running\n", card->name); 235 return 0; 236} 237 238/* ------------------------------------------------------------- */ 239 240int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) 241{ 242 avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); 243 avmcard *card = cinfo->card; 244 unsigned int port = card->port; 245 unsigned long flags; 246 int retval; 247 248 b1_reset(port); 249 250 if ((retval = b1_load_t4file(card, &data->firmware))) { 251 b1_reset(port); 252 printk(KERN_ERR "%s: failed to load t4file!!\n", 253 card->name); 254 return retval; 255 } 256 257 b1_disable_irq(port); 258 259 if (data->configuration.len > 0 && data->configuration.data) { 260 if ((retval = b1_load_config(card, &data->configuration))) { 261 b1_reset(port); 262 printk(KERN_ERR "%s: failed to load config!!\n", 263 card->name); 264 return retval; 265 } 266 } 267 268 if (!b1_loaded(card)) { 269 printk(KERN_ERR "%s: failed to load t4file.\n", card->name); 270 return -EIO; 271 } 272 273 save_flags(flags); 274 cli(); 275 b1_setinterrupt(port, card->irq, card->cardtype); 276 b1_put_byte(port, SEND_INIT); 277 b1_put_word(port, CAPI_MAXAPPL); 278 b1_put_word(port, AVM_NCCI_PER_CHANNEL*2); 279 b1_put_word(port, ctrl->cnr - 1); 280 restore_flags(flags); 281 282 return 0; 283} 284 285void b1_reset_ctr(struct capi_ctr *ctrl) 286{ 287 avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); 288 avmcard *card = cinfo->card; 289 unsigned int port = card->port; 290 291 b1_reset(port); 292 b1_reset(port); 293 294 memset(cinfo->version, 0, sizeof(cinfo->version)); 295 ctrl->reseted(ctrl); 296} 297 298void b1_register_appl(struct capi_ctr *ctrl, 299 __u16 appl, 300 capi_register_params *rp) 301{ 302 avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); 303 avmcard *card = cinfo->card; 304 unsigned int port = card->port; 305 unsigned long flags; 306 int nconn, want = rp->level3cnt; 307 308 if (want > 0) nconn = want; 309 else nconn = ctrl->profile.nbchannel * -want; 310 if (nconn == 0) nconn = ctrl->profile.nbchannel; 311 312 save_flags(flags); 313 cli(); 314 b1_put_byte(port, SEND_REGISTER); 315 b1_put_word(port, appl); 316 b1_put_word(port, 1024 * (nconn+1)); 317 b1_put_word(port, nconn); 318 b1_put_word(port, rp->datablkcnt); 319 b1_put_word(port, rp->datablklen); 320 restore_flags(flags); 321 322 ctrl->appl_registered(ctrl, appl); 323} 324 325void b1_release_appl(struct capi_ctr *ctrl, __u16 appl) 326{ 327 avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); 328 avmcard *card = cinfo->card; 329 unsigned int port = card->port; 330 unsigned long flags; 331 332 save_flags(flags); 333 cli(); 334 b1_put_byte(port, SEND_RELEASE); 335 b1_put_word(port, appl); 336 restore_flags(flags); 337} 338 339void b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) 340{ 341 avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); 342 avmcard *card = cinfo->card; 343 unsigned int port = card->port; 344 unsigned long flags; 345 __u16 len = CAPIMSG_LEN(skb->data); 346 __u8 cmd = CAPIMSG_COMMAND(skb->data); 347 __u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data); 348 349 save_flags(flags); 350 cli(); 351 if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { 352 __u16 dlen = CAPIMSG_DATALEN(skb->data); 353 b1_put_byte(port, SEND_DATA_B3_REQ); 354 b1_put_slice(port, skb->data, len); 355 b1_put_slice(port, skb->data + len, dlen); 356 } else { 357 b1_put_byte(port, SEND_MESSAGE); 358 b1_put_slice(port, skb->data, len); 359 } 360 restore_flags(flags); 361 dev_kfree_skb_any(skb); 362} 363 364/* ------------------------------------------------------------- */ 365 366void b1_parse_version(avmctrl_info *cinfo) 367{ 368 struct capi_ctr *ctrl = cinfo->capi_ctrl; 369 avmcard *card = cinfo->card; 370 capi_profile *profp; 371 __u8 *dversion; 372 __u8 flag; 373 int i, j; 374 375 for (j = 0; j < AVM_MAXVERSION; j++) 376 cinfo->version[j] = "\0\0" + 1; 377 for (i = 0, j = 0; 378 j < AVM_MAXVERSION && i < cinfo->versionlen; 379 j++, i += cinfo->versionbuf[i] + 1) 380 cinfo->version[j] = &cinfo->versionbuf[i + 1]; 381 382 strncpy(ctrl->serial, cinfo->version[VER_SERIAL], CAPI_SERIAL_LEN); 383 memcpy(&ctrl->profile, cinfo->version[VER_PROFILE],sizeof(capi_profile)); 384 strncpy(ctrl->manu, "AVM GmbH", CAPI_MANUFACTURER_LEN); 385 dversion = cinfo->version[VER_DRIVER]; 386 ctrl->version.majorversion = 2; 387 ctrl->version.minorversion = 0; 388 ctrl->version.majormanuversion = (((dversion[0] - '0') & 0xf) << 4); 389 ctrl->version.majormanuversion |= ((dversion[2] - '0') & 0xf); 390 ctrl->version.minormanuversion = (dversion[3] - '0') << 4; 391 ctrl->version.minormanuversion |= 392 (dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf); 393 394 profp = &ctrl->profile; 395 396 flag = ((__u8 *)(profp->manu))[1]; 397 switch (flag) { 398 case 0: if (cinfo->version[VER_CARDTYPE]) 399 strcpy(cinfo->cardname, cinfo->version[VER_CARDTYPE]); 400 else strcpy(cinfo->cardname, "B1"); 401 break; 402 case 3: strcpy(cinfo->cardname,"PCMCIA B"); break; 403 case 4: strcpy(cinfo->cardname,"PCMCIA M1"); break; 404 case 5: strcpy(cinfo->cardname,"PCMCIA M2"); break; 405 case 6: strcpy(cinfo->cardname,"B1 V3.0"); break; 406 case 7: strcpy(cinfo->cardname,"B1 PCI"); break; 407 default: sprintf(cinfo->cardname, "AVM?%u", (unsigned int)flag); break; 408 } 409 printk(KERN_NOTICE "%s: card %d \"%s\" ready.\n", 410 card->name, ctrl->cnr, cinfo->cardname); 411 412 flag = ((__u8 *)(profp->manu))[3]; 413 if (flag) 414 printk(KERN_NOTICE "%s: card %d Protocol:%s%s%s%s%s%s%s\n", 415 card->name, 416 ctrl->cnr, 417 (flag & 0x01) ? " DSS1" : "", 418 (flag & 0x02) ? " CT1" : "", 419 (flag & 0x04) ? " VN3" : "", 420 (flag & 0x08) ? " NI1" : "", 421 (flag & 0x10) ? " AUSTEL" : "", 422 (flag & 0x20) ? " ESS" : "", 423 (flag & 0x40) ? " 1TR6" : "" 424 ); 425 426 flag = ((__u8 *)(profp->manu))[5]; 427 if (flag) 428 printk(KERN_NOTICE "%s: card %d Linetype:%s%s%s%s\n", 429 card->name, 430 ctrl->cnr, 431 (flag & 0x01) ? " point to point" : "", 432 (flag & 0x02) ? " point to multipoint" : "", 433 (flag & 0x08) ? " leased line without D-channel" : "", 434 (flag & 0x04) ? " leased line with D-channel" : "" 435 ); 436} 437 438/* ------------------------------------------------------------- */ 439 440void b1_handle_interrupt(avmcard * card) 441{ 442 avmctrl_info *cinfo = &card->ctrlinfo[0]; 443 struct capi_ctr *ctrl = cinfo->capi_ctrl; 444 unsigned char b1cmd; 445 struct sk_buff *skb; 446 447 unsigned ApplId; 448 unsigned MsgLen; 449 unsigned DataB3Len; 450 unsigned NCCI; 451 unsigned WindowSize; 452 453 if (!b1_rx_full(card->port)) 454 return; 455 456 b1cmd = b1_get_byte(card->port); 457 458 switch (b1cmd) { 459 460 case RECEIVE_DATA_B3_IND: 461 462 ApplId = (unsigned) b1_get_word(card->port); 463 MsgLen = b1_get_slice(card->port, card->msgbuf); 464 DataB3Len = b1_get_slice(card->port, card->databuf); 465 466 if (MsgLen < 30) { /* not CAPI 64Bit */ 467 memset(card->msgbuf+MsgLen, 0, 30-MsgLen); 468 MsgLen = 30; 469 CAPIMSG_SETLEN(card->msgbuf, 30); 470 } 471 if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) { 472 printk(KERN_ERR "%s: incoming packet dropped\n", 473 card->name); 474 } else { 475 memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); 476 memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len); 477 ctrl->handle_capimsg(ctrl, ApplId, skb); 478 } 479 break; 480 481 case RECEIVE_MESSAGE: 482 483 ApplId = (unsigned) b1_get_word(card->port); 484 MsgLen = b1_get_slice(card->port, card->msgbuf); 485 if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { 486 printk(KERN_ERR "%s: incoming packet dropped\n", 487 card->name); 488 } else { 489 memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); 490 ctrl->handle_capimsg(ctrl, ApplId, skb); 491 } 492 break; 493 494 case RECEIVE_NEW_NCCI: 495 496 ApplId = b1_get_word(card->port); 497 NCCI = b1_get_word(card->port); 498 WindowSize = b1_get_word(card->port); 499 500 ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize); 501 502 break; 503 504 case RECEIVE_FREE_NCCI: 505 506 ApplId = b1_get_word(card->port); 507 NCCI = b1_get_word(card->port); 508 509 if (NCCI != 0xffffffff) 510 ctrl->free_ncci(ctrl, ApplId, NCCI); 511 else ctrl->appl_released(ctrl, ApplId); 512 break; 513 514 case RECEIVE_START: 515 /* b1_put_byte(card->port, SEND_POLLACK); */ 516 ctrl->resume_output(ctrl); 517 break; 518 519 case RECEIVE_STOP: 520 ctrl->suspend_output(ctrl); 521 break; 522 523 case RECEIVE_INIT: 524 525 cinfo->versionlen = b1_get_slice(card->port, cinfo->versionbuf); 526 b1_parse_version(cinfo); 527 printk(KERN_INFO "%s: %s-card (%s) now active\n", 528 card->name, 529 cinfo->version[VER_CARDTYPE], 530 cinfo->version[VER_DRIVER]); 531 ctrl->ready(ctrl); 532 break; 533 534 case RECEIVE_TASK_READY: 535 ApplId = (unsigned) b1_get_word(card->port); 536 MsgLen = b1_get_slice(card->port, card->msgbuf); 537 card->msgbuf[MsgLen] = 0; 538 while ( MsgLen > 0 539 && ( card->msgbuf[MsgLen-1] == '\n' 540 || card->msgbuf[MsgLen-1] == '\r')) { 541 card->msgbuf[MsgLen-1] = 0; 542 MsgLen--; 543 } 544 printk(KERN_INFO "%s: task %d \"%s\" ready.\n", 545 card->name, ApplId, card->msgbuf); 546 break; 547 548 case RECEIVE_DEBUGMSG: 549 MsgLen = b1_get_slice(card->port, card->msgbuf); 550 card->msgbuf[MsgLen] = 0; 551 while ( MsgLen > 0 552 && ( card->msgbuf[MsgLen-1] == '\n' 553 || card->msgbuf[MsgLen-1] == '\r')) { 554 card->msgbuf[MsgLen-1] = 0; 555 MsgLen--; 556 } 557 printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf); 558 break; 559 560 case 0xff: 561 printk(KERN_ERR "%s: card removed ?\n", card->name); 562 return; 563 default: 564 printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n", 565 card->name, b1cmd); 566 return; 567 } 568} 569 570/* ------------------------------------------------------------- */ 571int b1ctl_read_proc(char *page, char **start, off_t off, 572 int count, int *eof, struct capi_ctr *ctrl) 573{ 574 avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); 575 avmcard *card = cinfo->card; 576 __u8 flag; 577 int len = 0; 578 char *s; 579 580 len += sprintf(page+len, "%-16s %s\n", "name", card->name); 581 len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port); 582 len += sprintf(page+len, "%-16s %d\n", "irq", card->irq); 583 switch (card->cardtype) { 584 case avm_b1isa: s = "B1 ISA"; break; 585 case avm_b1pci: s = "B1 PCI"; break; 586 case avm_b1pcmcia: s = "B1 PCMCIA"; break; 587 case avm_m1: s = "M1"; break; 588 case avm_m2: s = "M2"; break; 589 case avm_t1isa: s = "T1 ISA (HEMA)"; break; 590 case avm_t1pci: s = "T1 PCI"; break; 591 case avm_c4: s = "C4"; break; 592 case avm_c2: s = "C2"; break; 593 default: s = "???"; break; 594 } 595 len += sprintf(page+len, "%-16s %s\n", "type", s); 596 if (card->cardtype == avm_t1isa) 597 len += sprintf(page+len, "%-16s %d\n", "cardnr", card->cardnr); 598 if ((s = cinfo->version[VER_DRIVER]) != 0) 599 len += sprintf(page+len, "%-16s %s\n", "ver_driver", s); 600 if ((s = cinfo->version[VER_CARDTYPE]) != 0) 601 len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s); 602 if ((s = cinfo->version[VER_SERIAL]) != 0) 603 len += sprintf(page+len, "%-16s %s\n", "ver_serial", s); 604 605 if (card->cardtype != avm_m1) { 606 flag = ((__u8 *)(ctrl->profile.manu))[3]; 607 if (flag) 608 len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n", 609 "protocol", 610 (flag & 0x01) ? " DSS1" : "", 611 (flag & 0x02) ? " CT1" : "", 612 (flag & 0x04) ? " VN3" : "", 613 (flag & 0x08) ? " NI1" : "", 614 (flag & 0x10) ? " AUSTEL" : "", 615 (flag & 0x20) ? " ESS" : "", 616 (flag & 0x40) ? " 1TR6" : "" 617 ); 618 } 619 if (card->cardtype != avm_m1) { 620 flag = ((__u8 *)(ctrl->profile.manu))[5]; 621 if (flag) 622 len += sprintf(page+len, "%-16s%s%s%s%s\n", 623 "linetype", 624 (flag & 0x01) ? " point to point" : "", 625 (flag & 0x02) ? " point to multipoint" : "", 626 (flag & 0x08) ? " leased line without D-channel" : "", 627 (flag & 0x04) ? " leased line with D-channel" : "" 628 ); 629 } 630 len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname); 631 632 if (off+count >= len) 633 *eof = 1; 634 if (len < off) 635 return 0; 636 *start = page + off; 637 return ((count < len-off) ? count : len-off); 638} 639 640/* ------------------------------------------------------------- */ 641 642EXPORT_SYMBOL(b1_irq_table); 643 644EXPORT_SYMBOL(b1_detect); 645EXPORT_SYMBOL(b1_getrevision); 646EXPORT_SYMBOL(b1_load_t4file); 647EXPORT_SYMBOL(b1_load_config); 648EXPORT_SYMBOL(b1_loaded); 649EXPORT_SYMBOL(b1_load_firmware); 650EXPORT_SYMBOL(b1_reset_ctr); 651EXPORT_SYMBOL(b1_register_appl); 652EXPORT_SYMBOL(b1_release_appl); 653EXPORT_SYMBOL(b1_send_message); 654 655EXPORT_SYMBOL(b1_parse_version); 656EXPORT_SYMBOL(b1_handle_interrupt); 657 658EXPORT_SYMBOL(b1ctl_read_proc); 659 660static int __init b1_init(void) 661{ 662 char *p; 663 char rev[32]; 664 665 if ((p = strchr(revision, ':')) != 0 && p[1]) { 666 strncpy(rev, p + 2, sizeof(rev)); 667 rev[sizeof(rev)-1] = 0; 668 if ((p = strchr(rev, '$')) != 0 && p > rev) 669 *(p-1) = 0; 670 } else 671 strcpy(rev, "1.0"); 672 673 printk(KERN_INFO "b1: revision %s\n", rev); 674 675 return 0; 676} 677 678static void __exit b1_exit(void) 679{ 680} 681 682module_init(b1_init); 683module_exit(b1_exit); 684