1/* 2 * Hardware-level driver for the COMX and HICOMX cards 3 * for Linux kernel 2.2.X 4 * 5 * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>, 6 * Peter Bajan <bajan.peter@synergon.hu>, 7 * Rewritten by: Tivadar Szemethy <tiv@itc.hu> 8 * Currently maintained by: Gergely Madarasz <gorgo@itc.hu> 9 * 10 * Copyright (C) 1995-2000 ITConsult-Pro Co. <info@itc.hu> 11 * 12 * Contributors: 13 * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 0.86 14 * 15 * This program is free software; you can redistribute it and/or 16 * modify it under the terms of the GNU General Public License 17 * as published by the Free Software Foundation; either version 18 * 2 of the License, or (at your option) any later version. 19 * 20 * Version 0.80 (99/06/11): 21 * - port back to kernel, add support builtin driver 22 * - cleaned up the source code a bit 23 * 24 * Version 0.81 (99/06/22): 25 * - cleaned up the board load functions, no more long reset 26 * timeouts 27 * - lower modem lines on close 28 * - some interrupt handling fixes 29 * 30 * Version 0.82 (99/08/24): 31 * - fix multiple board support 32 * 33 * Version 0.83 (99/11/30): 34 * - interrupt handling and locking fixes during initalization 35 * - really fix multiple board support 36 * 37 * Version 0.84 (99/12/02): 38 * - some workarounds for problematic hardware/firmware 39 * 40 * Version 0.85 (00/01/14): 41 * - some additional workarounds :/ 42 * - printk cleanups 43 * Version 0.86 (00/08/15): 44 * - resource release on failure at COMX_init 45 */ 46 47#define VERSION "0.86" 48 49#include <linux/module.h> 50#include <linux/version.h> 51#include <linux/types.h> 52#include <linux/sched.h> 53#include <linux/netdevice.h> 54#include <linux/proc_fs.h> 55#include <linux/ioport.h> 56#include <linux/init.h> 57#include <linux/delay.h> 58#include <asm/uaccess.h> 59#include <asm/io.h> 60 61#include "comx.h" 62#include "comxhw.h" 63 64MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>, Tivadar Szemethy <tiv@itc.hu>, Arpad Bakay"); 65MODULE_DESCRIPTION("Hardware-level driver for the COMX and HICOMX adapters\n"); 66MODULE_LICENSE("GPL"); 67 68#define COMX_readw(dev, offset) (readw(dev->mem_start + offset + \ 69 (unsigned int)(((struct comx_privdata *)\ 70 ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \ 71 * COMX_CHANNEL_OFFSET)) 72 73#define COMX_WRITE(dev, offset, value) (writew(value, dev->mem_start + offset \ 74 + (unsigned int)(((struct comx_privdata *) \ 75 ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \ 76 * COMX_CHANNEL_OFFSET)) 77 78#define COMX_CMD(dev, cmd) (COMX_WRITE(dev, OFF_A_L2_CMD, cmd)) 79 80struct comx_firmware { 81 int len; 82 unsigned char *data; 83}; 84 85struct comx_privdata { 86 struct comx_firmware *firmware; 87 u16 clock; 88 char channel; // channel no. 89 int memory_size; 90 short io_extent; 91 u_long histogram[5]; 92}; 93 94static struct net_device *memory_used[(COMX_MEM_MAX - COMX_MEM_MIN) / 0x10000]; 95extern struct comx_hardware hicomx_hw; 96extern struct comx_hardware comx_hw; 97extern struct comx_hardware cmx_hw; 98 99static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs); 100 101static void COMX_board_on(struct net_device *dev) 102{ 103 outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | 104 COMX_ENABLE_BOARD_IT | COMX_ENABLE_BOARD_MEM), dev->base_addr); 105} 106 107static void COMX_board_off(struct net_device *dev) 108{ 109 outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | 110 COMX_ENABLE_BOARD_IT), dev->base_addr); 111} 112 113static void HICOMX_board_on(struct net_device *dev) 114{ 115 outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | 116 HICOMX_ENABLE_BOARD_MEM), dev->base_addr); 117} 118 119static void HICOMX_board_off(struct net_device *dev) 120{ 121 outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | 122 HICOMX_DISABLE_BOARD_MEM), dev->base_addr); 123} 124 125static void COMX_set_clock(struct net_device *dev) 126{ 127 struct comx_channel *ch = dev->priv; 128 struct comx_privdata *hw = ch->HW_privdata; 129 130 COMX_WRITE(dev, OFF_A_L1_CLKINI, hw->clock); 131} 132 133static struct net_device *COMX_access_board(struct net_device *dev) 134{ 135 struct comx_channel *ch = dev->priv; 136 struct net_device *ret; 137 int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16; 138 unsigned long flags; 139 140 141 save_flags(flags); cli(); 142 143 ret = memory_used[mempos]; 144 145 if(ret == dev) { 146 goto out; 147 } 148 149 memory_used[mempos] = dev; 150 151 if (!ch->twin || ret != ch->twin) { 152 if (ret) ((struct comx_channel *)ret->priv)->HW_board_off(ret); 153 ch->HW_board_on(dev); 154 } 155out: 156 restore_flags(flags); 157 return ret; 158} 159 160static void COMX_release_board(struct net_device *dev, struct net_device *savep) 161{ 162 unsigned long flags; 163 int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16; 164 struct comx_channel *ch = dev->priv; 165 166 save_flags(flags); cli(); 167 168 if (memory_used[mempos] == savep) { 169 goto out; 170 } 171 172 memory_used[mempos] = savep; 173 if (!ch->twin || ch->twin != savep) { 174 ch->HW_board_off(dev); 175 if (savep) ((struct comx_channel*)savep->priv)->HW_board_on(savep); 176 } 177out: 178 restore_flags(flags); 179} 180 181static int COMX_txe(struct net_device *dev) 182{ 183 struct net_device *savep; 184 struct comx_channel *ch = dev->priv; 185 int rc = 0; 186 187 savep = ch->HW_access_board(dev); 188 if (COMX_readw(dev,OFF_A_L2_LINKUP) == LINKUP_READY) { 189 rc = COMX_readw(dev,OFF_A_L2_TxEMPTY); 190 } 191 ch->HW_release_board(dev,savep); 192 if(rc==0xffff) { 193 printk(KERN_ERR "%s, OFF_A_L2_TxEMPTY is %d\n",dev->name, rc); 194 } 195 return rc; 196} 197 198static int COMX_send_packet(struct net_device *dev, struct sk_buff *skb) 199{ 200 struct net_device *savep; 201 struct comx_channel *ch = dev->priv; 202 struct comx_privdata *hw = ch->HW_privdata; 203 int ret = FRAME_DROPPED; 204 word tmp; 205 206 savep = ch->HW_access_board(dev); 207 208 if (ch->debug_flags & DEBUG_HW_TX) { 209 comx_debug_bytes(dev, skb->data, skb->len,"COMX_send packet"); 210 } 211 212 if (skb->len > COMX_MAX_TX_SIZE) { 213 ret=FRAME_DROPPED; 214 goto out; 215 } 216 217 tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY); 218 if ((ch->line_status & LINE_UP) && tmp==1) { 219 int lensave = skb->len; 220 int dest = COMX_readw(dev, OFF_A_L2_TxBUFP); 221 word *data = (word *)skb->data; 222 223 if(dest==0xffff) { 224 printk(KERN_ERR "%s: OFF_A_L2_TxBUFP is %d\n", dev->name, dest); 225 ret=FRAME_DROPPED; 226 goto out; 227 } 228 229 writew((unsigned short)skb->len, dev->mem_start + dest); 230 dest += 2; 231 while (skb->len > 1) { 232 writew(*data++, dev->mem_start + dest); 233 dest += 2; skb->len -= 2; 234 } 235 if (skb->len == 1) { 236 writew(*((byte *)data), dev->mem_start + dest); 237 } 238 writew(0, dev->mem_start + (int)hw->channel * 239 COMX_CHANNEL_OFFSET + OFF_A_L2_TxEMPTY); 240 ch->stats.tx_packets++; 241 ch->stats.tx_bytes += lensave; 242 ret = FRAME_ACCEPTED; 243 } else { 244 ch->stats.tx_dropped++; 245 printk(KERN_INFO "%s: frame dropped\n",dev->name); 246 if(tmp) { 247 printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n",dev->name,tmp); 248 } 249 } 250 251out: 252 ch->HW_release_board(dev, savep); 253 dev_kfree_skb(skb); 254 return ret; 255} 256 257static inline int comx_read_buffer(struct net_device *dev) 258{ 259 struct comx_channel *ch = dev->priv; 260 word rbuf_offs; 261 struct sk_buff *skb; 262 word len; 263 int i=0; 264 word *writeptr; 265 266 i = 0; 267 rbuf_offs = COMX_readw(dev, OFF_A_L2_RxBUFP); 268 if(rbuf_offs == 0xffff) { 269 printk(KERN_ERR "%s: OFF_A_L2_RxBUFP is %d\n",dev->name,rbuf_offs); 270 return 0; 271 } 272 len = readw(dev->mem_start + rbuf_offs); 273 if(len > COMX_MAX_RX_SIZE) { 274 printk(KERN_ERR "%s: packet length is %d\n",dev->name,len); 275 return 0; 276 } 277 if ((skb = dev_alloc_skb(len + 16)) == NULL) { 278 ch->stats.rx_dropped++; 279 COMX_WRITE(dev, OFF_A_L2_DAV, 0); 280 return 0; 281 } 282 rbuf_offs += 2; 283 skb_reserve(skb, 16); 284 skb_put(skb, len); 285 skb->dev = dev; 286 writeptr = (word *)skb->data; 287 while (i < len) { 288 *writeptr++ = readw(dev->mem_start + rbuf_offs); 289 rbuf_offs += 2; 290 i += 2; 291 } 292 COMX_WRITE(dev, OFF_A_L2_DAV, 0); 293 ch->stats.rx_packets++; 294 ch->stats.rx_bytes += len; 295 if (ch->debug_flags & DEBUG_HW_RX) { 296 comx_debug_skb(dev, skb, "COMX_interrupt receiving"); 297 } 298 ch->LINE_rx(dev, skb); 299 return 1; 300} 301 302static inline char comx_line_change(struct net_device *dev, char linestat) 303{ 304 struct comx_channel *ch=dev->priv; 305 char idle=1; 306 307 308 if (linestat & LINE_UP) { /* Vonal fol */ 309 if (ch->lineup_delay) { 310 if (!test_and_set_bit(0, &ch->lineup_pending)) { 311 ch->lineup_timer.function = comx_lineup_func; 312 ch->lineup_timer.data = (unsigned long)dev; 313 ch->lineup_timer.expires = jiffies + 314 HZ*ch->lineup_delay; 315 add_timer(&ch->lineup_timer); 316 idle=0; 317 } 318 } else { 319 idle=0; 320 ch->LINE_status(dev, ch->line_status |= LINE_UP); 321 } 322 } else { /* Vonal le */ 323 idle=0; 324 if (test_and_clear_bit(0, &ch->lineup_pending)) { 325 del_timer(&ch->lineup_timer); 326 } else { 327 ch->line_status &= ~LINE_UP; 328 if (ch->LINE_status) { 329 ch->LINE_status(dev, ch->line_status); 330 } 331 } 332 } 333 return idle; 334} 335 336 337 338static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs) 339{ 340 struct net_device *dev = dev_id; 341 struct comx_channel *ch = dev->priv; 342 struct comx_privdata *hw = ch->HW_privdata; 343 struct net_device *interrupted; 344 unsigned long jiffs; 345 char idle = 0; 346 int count = 0; 347 word tmp; 348 349 if (dev == NULL) { 350 printk(KERN_ERR "COMX_interrupt: irq %d for unknown device\n", irq); 351 return; 352 } 353 354 jiffs = jiffies; 355 356 interrupted = ch->HW_access_board(dev); 357 358 while (!idle && count < 5000) { 359 char channel = 0; 360 idle = 1; 361 362 while (channel < 2) { 363 char linestat = 0; 364 char buffers_emptied = 0; 365 366 if (channel == 1) { 367 if (ch->twin) { 368 dev = ch->twin; 369 ch = dev->priv; 370 hw = ch->HW_privdata; 371 } else { 372 break; 373 } 374 } else { 375 COMX_WRITE(dev, OFF_A_L1_REPENA, 376 COMX_readw(dev, OFF_A_L1_REPENA) & 0xFF00); 377 } 378 channel++; 379 380 if ((ch->init_status & (HW_OPEN | LINE_OPEN)) != 381 (HW_OPEN | LINE_OPEN)) { 382 continue; 383 } 384 385 /* Collect stats */ 386 tmp = COMX_readw(dev, OFF_A_L1_ABOREC); 387 COMX_WRITE(dev, OFF_A_L1_ABOREC, 0); 388 if(tmp==0xffff) { 389 printk(KERN_ERR "%s: OFF_A_L1_ABOREC is %d\n",dev->name,tmp); 390 break; 391 } else { 392 ch->stats.rx_missed_errors += (tmp >> 8) & 0xff; 393 ch->stats.rx_over_errors += tmp & 0xff; 394 } 395 tmp = COMX_readw(dev, OFF_A_L1_CRCREC); 396 COMX_WRITE(dev, OFF_A_L1_CRCREC, 0); 397 if(tmp==0xffff) { 398 printk(KERN_ERR "%s: OFF_A_L1_CRCREC is %d\n",dev->name,tmp); 399 break; 400 } else { 401 ch->stats.rx_crc_errors += (tmp >> 8) & 0xff; 402 ch->stats.rx_missed_errors += tmp & 0xff; 403 } 404 405 if ((ch->line_status & LINE_UP) && ch->LINE_rx) { 406 tmp=COMX_readw(dev, OFF_A_L2_DAV); 407 while (tmp==1) { 408 idle=0; 409 buffers_emptied+=comx_read_buffer(dev); 410 tmp=COMX_readw(dev, OFF_A_L2_DAV); 411 } 412 if(tmp) { 413 printk(KERN_ERR "%s: OFF_A_L2_DAV is %d\n", dev->name, tmp); 414 break; 415 } 416 } 417 418 tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY); 419 if (tmp==1 && ch->LINE_tx) { 420 ch->LINE_tx(dev); 421 } 422 if(tmp==0xffff) { 423 printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n", dev->name, tmp); 424 break; 425 } 426 427 if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) { 428 linestat &= ~LINE_UP; 429 } else { 430 linestat |= LINE_UP; 431 } 432 433 if ((linestat & LINE_UP) != (ch->line_status & LINE_UP)) { 434 ch->stats.tx_carrier_errors++; 435 idle &= comx_line_change(dev,linestat); 436 } 437 438 hw->histogram[(int)buffers_emptied]++; 439 } 440 count++; 441 } 442 443 if(count==5000) { 444 printk(KERN_WARNING "%s: interrupt stuck\n",dev->name); 445 } 446 447 ch->HW_release_board(dev, interrupted); 448} 449 450static int COMX_open(struct net_device *dev) 451{ 452 struct comx_channel *ch = dev->priv; 453 struct comx_privdata *hw = ch->HW_privdata; 454 struct proc_dir_entry *procfile = ch->procdir->subdir; 455 unsigned long jiffs; 456 int twin_open=0; 457 int retval; 458 struct net_device *savep; 459 460 if (!dev->base_addr || !dev->irq || !dev->mem_start) { 461 return -ENODEV; 462 } 463 464 if (ch->twin && (((struct comx_channel *)(ch->twin->priv))->init_status & HW_OPEN)) { 465 twin_open=1; 466 } 467 468 if (!twin_open) { 469 if (!request_region(dev->base_addr, hw->io_extent, dev->name)) { 470 return -EAGAIN; 471 } 472 if (request_irq(dev->irq, COMX_interrupt, 0, dev->name, 473 (void *)dev)) { 474 printk(KERN_ERR "comx-hw-comx: unable to obtain irq %d\n", dev->irq); 475 release_region(dev->base_addr, hw->io_extent); 476 return -EAGAIN; 477 } 478 ch->init_status |= IRQ_ALLOCATED; 479 if (!ch->HW_load_board || ch->HW_load_board(dev)) { 480 ch->init_status &= ~IRQ_ALLOCATED; 481 retval=-ENODEV; 482 goto error; 483 } 484 } 485 486 savep = ch->HW_access_board(dev); 487 COMX_WRITE(dev, OFF_A_L2_LINKUP, 0); 488 489 if (ch->HW_set_clock) { 490 ch->HW_set_clock(dev); 491 } 492 493 COMX_CMD(dev, COMX_CMD_INIT); 494 jiffs = jiffies; 495 while (COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiffs + HZ) { 496 schedule_timeout(1); 497 } 498 499 if (jiffies >= jiffs + HZ) { 500 printk(KERN_ERR "%s: board timeout on INIT command\n", dev->name); 501 ch->HW_release_board(dev, savep); 502 retval=-EIO; 503 goto error; 504 } 505 udelay(1000); 506 507 COMX_CMD(dev, COMX_CMD_OPEN); 508 509 jiffs = jiffies; 510 while (COMX_readw(dev, OFF_A_L2_LINKUP) != 3 && jiffies < jiffs + HZ) { 511 schedule_timeout(1); 512 } 513 514 if (jiffies >= jiffs + HZ) { 515 printk(KERN_ERR "%s: board timeout on OPEN command\n", dev->name); 516 ch->HW_release_board(dev, savep); 517 retval=-EIO; 518 goto error; 519 } 520 521 ch->init_status |= HW_OPEN; 522 523 /* Ez eleg ciki, de ilyen a rendszer */ 524 if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) { 525 ch->line_status &= ~LINE_UP; 526 } else { 527 ch->line_status |= LINE_UP; 528 } 529 530 if (ch->LINE_status) { 531 ch->LINE_status(dev, ch->line_status); 532 } 533 534 ch->HW_release_board(dev, savep); 535 536 for ( ; procfile ; procfile = procfile->next) { 537 if (strcmp(procfile->name, FILENAME_IRQ) == 0 538 || strcmp(procfile->name, FILENAME_IO) == 0 539 || strcmp(procfile->name, FILENAME_MEMADDR) == 0 540 || strcmp(procfile->name, FILENAME_CHANNEL) == 0 541 || strcmp(procfile->name, FILENAME_FIRMWARE) == 0 542 || strcmp(procfile->name, FILENAME_CLOCK) == 0) { 543 procfile->mode = S_IFREG | 0444; 544 545 } 546 } 547 548 return 0; 549 550error: 551 if(!twin_open) { 552 release_region(dev->base_addr, hw->io_extent); 553 free_irq(dev->irq, (void *)dev); 554 } 555 return retval; 556 557} 558 559static int COMX_close(struct net_device *dev) 560{ 561 struct comx_channel *ch = dev->priv; 562 struct proc_dir_entry *procfile = ch->procdir->subdir; 563 struct comx_privdata *hw = ch->HW_privdata; 564 struct comx_channel *twin_ch; 565 struct net_device *savep; 566 567 savep = ch->HW_access_board(dev); 568 569 COMX_CMD(dev, COMX_CMD_CLOSE); 570 udelay(1000); 571 COMX_CMD(dev, COMX_CMD_EXIT); 572 573 ch->HW_release_board(dev, savep); 574 575 if (ch->init_status & IRQ_ALLOCATED) { 576 free_irq(dev->irq, (void *)dev); 577 ch->init_status &= ~IRQ_ALLOCATED; 578 } 579 release_region(dev->base_addr, hw->io_extent); 580 581 if (ch->twin && (twin_ch = ch->twin->priv) && 582 (twin_ch->init_status & HW_OPEN)) { 583 /* Pass the irq to the twin */ 584 if (request_irq(dev->irq, COMX_interrupt, 0, ch->twin->name, 585 (void *)ch->twin) == 0) { 586 twin_ch->init_status |= IRQ_ALLOCATED; 587 } 588 } 589 590 for ( ; procfile ; procfile = procfile->next) { 591 if (strcmp(procfile->name, FILENAME_IRQ) == 0 592 || strcmp(procfile->name, FILENAME_IO) == 0 593 || strcmp(procfile->name, FILENAME_MEMADDR) == 0 594 || strcmp(procfile->name, FILENAME_CHANNEL) == 0 595 || strcmp(procfile->name, FILENAME_FIRMWARE) == 0 596 || strcmp(procfile->name, FILENAME_CLOCK) == 0) { 597 procfile->mode = S_IFREG | 0644; 598 } 599 } 600 601 ch->init_status &= ~HW_OPEN; 602 return 0; 603} 604 605static int COMX_statistics(struct net_device *dev, char *page) 606{ 607 struct comx_channel *ch = dev->priv; 608 struct comx_privdata *hw = ch->HW_privdata; 609 struct net_device *savep; 610 int len = 0; 611 612 savep = ch->HW_access_board(dev); 613 614 len += sprintf(page + len, "Board data: %s %s %s %s\nPBUFOVR: %02x, " 615 "MODSTAT: %02x, LINKUP: %02x, DAV: %02x\nRxBUFP: %02x, " 616 "TxEMPTY: %02x, TxBUFP: %02x\n", 617 (ch->init_status & HW_OPEN) ? "HW_OPEN" : "", 618 (ch->init_status & LINE_OPEN) ? "LINE_OPEN" : "", 619 (ch->init_status & FW_LOADED) ? "FW_LOADED" : "", 620 (ch->init_status & IRQ_ALLOCATED) ? "IRQ_ALLOCATED" : "", 621 COMX_readw(dev, OFF_A_L1_PBUFOVR) & 0xff, 622 (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) & 0xff, 623 COMX_readw(dev, OFF_A_L2_LINKUP) & 0xff, 624 COMX_readw(dev, OFF_A_L2_DAV) & 0xff, 625 COMX_readw(dev, OFF_A_L2_RxBUFP) & 0xff, 626 COMX_readw(dev, OFF_A_L2_TxEMPTY) & 0xff, 627 COMX_readw(dev, OFF_A_L2_TxBUFP) & 0xff); 628 629 len += sprintf(page + len, "hist[0]: %8lu hist[1]: %8lu hist[2]: %8lu\n" 630 "hist[3]: %8lu hist[4]: %8lu\n",hw->histogram[0],hw->histogram[1], 631 hw->histogram[2],hw->histogram[3],hw->histogram[4]); 632 633 ch->HW_release_board(dev, savep); 634 635 return len; 636} 637 638static int COMX_load_board(struct net_device *dev) 639{ 640 struct comx_channel *ch = dev->priv; 641 struct comx_privdata *hw = ch->HW_privdata; 642 struct comx_firmware *fw = hw->firmware; 643 word board_segment = dev->mem_start >> 16; 644 int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16; 645 unsigned long flags; 646 unsigned char id1, id2; 647 struct net_device *saved; 648 int retval; 649 int loopcount; 650 int len; 651 byte *COMX_address; 652 653 if (!fw || !fw->len) { 654 struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL; 655 struct comx_privdata *twin_hw; 656 657 if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) { 658 return -EAGAIN; 659 } 660 661 if (!(fw = twin_hw->firmware) || !fw->len) { 662 return -EAGAIN; 663 } 664 } 665 666 id1 = fw->data[OFF_FW_L1_ID]; 667 id2 = fw->data[OFF_FW_L1_ID + 1]; 668 669 if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_COMX) { 670 printk(KERN_ERR "%s: incorrect firmware, load aborted\n", 671 dev->name); 672 return -EAGAIN; 673 } 674 675 printk(KERN_INFO "%s: Loading COMX Layer 1 firmware %s\n", dev->name, 676 (char *)(fw->data + OFF_FW_L1_ID + 2)); 677 678 id1 = fw->data[OFF_FW_L2_ID]; 679 id2 = fw->data[OFF_FW_L2_ID + 1]; 680 if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) { 681 printk(KERN_INFO "with Layer 2 code %s\n", 682 (char *)(fw->data + OFF_FW_L2_ID + 2)); 683 } 684 685 outb_p(board_segment | COMX_BOARD_RESET, dev->base_addr); 686 /* 10 usec should be enough here */ 687 udelay(100); 688 689 save_flags(flags); cli(); 690 saved=memory_used[mempos]; 691 if(saved) { 692 ((struct comx_channel *)saved->priv)->HW_board_off(saved); 693 } 694 memory_used[mempos]=dev; 695 696 outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr); 697 698 writeb(0, dev->mem_start + COMX_JAIL_OFFSET); 699 700 loopcount=0; 701 while(loopcount++ < 10000 && 702 readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) { 703 udelay(100); 704 } 705 706 if (readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) { 707 printk(KERN_ERR "%s: Can't reset board, JAIL value is %02x\n", 708 dev->name, readb(dev->mem_start + COMX_JAIL_OFFSET)); 709 retval=-ENODEV; 710 goto out; 711 } 712 713 writeb(0x55, dev->mem_start + 0x18ff); 714 715 loopcount=0; 716 while(loopcount++ < 10000 && readb(dev->mem_start + 0x18ff) != 0) { 717 udelay(100); 718 } 719 720 if(readb(dev->mem_start + 0x18ff) != 0) { 721 printk(KERN_ERR "%s: Can't reset board, reset timeout\n", 722 dev->name); 723 retval=-ENODEV; 724 goto out; 725 } 726 727 len = 0; 728 COMX_address = (byte *)dev->mem_start; 729 while (fw->len > len) { 730 writeb(fw->data[len++], COMX_address++); 731 } 732 733 len = 0; 734 COMX_address = (byte *)dev->mem_start; 735 while (len != fw->len && readb(COMX_address++) == fw->data[len]) { 736 len++; 737 } 738 739 if (len != fw->len) { 740 printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x " 741 "instead of 0x%02x\n", dev->name, len, 742 readb(COMX_address - 1), fw->data[len]); 743 retval=-EAGAIN; 744 goto out; 745 } 746 747 writeb(0, dev->mem_start + COMX_JAIL_OFFSET); 748 749 loopcount = 0; 750 while ( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) { 751 udelay(100); 752 } 753 754 if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) { 755 printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n", 756 dev->name, COMX_readw(dev, OFF_A_L2_LINKUP)); 757 retval=-EAGAIN; 758 goto out; 759 } 760 761 762 ch->init_status |= FW_LOADED; 763 retval=0; 764 765out: 766 outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr); 767 if(saved) { 768 ((struct comx_channel *)saved->priv)->HW_board_on(saved); 769 } 770 memory_used[mempos]=saved; 771 restore_flags(flags); 772 return retval; 773} 774 775static int CMX_load_board(struct net_device *dev) 776{ 777 struct comx_channel *ch = dev->priv; 778 struct comx_privdata *hw = ch->HW_privdata; 779 struct comx_firmware *fw = hw->firmware; 780 word board_segment = dev->mem_start >> 16; 781 int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16; 782 #if 0 783 unsigned char id1, id2; 784 #endif 785 struct net_device *saved; 786 unsigned long flags; 787 int retval; 788 int loopcount; 789 int len; 790 byte *COMX_address; 791 792 if (!fw || !fw->len) { 793 struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL; 794 struct comx_privdata *twin_hw; 795 796 if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) { 797 return -EAGAIN; 798 } 799 800 if (!(fw = twin_hw->firmware) || !fw->len) { 801 return -EAGAIN; 802 } 803 } 804 805 /* Ide kell olyat tenni, hogy ellenorizze az ID-t */ 806 807 if (inb_p(dev->base_addr) != CMX_ID_BYTE) { 808 printk(KERN_ERR "%s: CMX id byte is invalid(%02x)\n", dev->name, 809 inb_p(dev->base_addr)); 810 return -ENODEV; 811 } 812 813 printk(KERN_INFO "%s: Loading CMX Layer 1 firmware %s\n", dev->name, 814 (char *)(fw->data + OFF_FW_L1_ID + 2)); 815 816 save_flags(flags); cli(); 817 saved=memory_used[mempos]; 818 if(saved) { 819 ((struct comx_channel *)saved->priv)->HW_board_off(saved); 820 } 821 memory_used[mempos]=dev; 822 823 outb_p(board_segment | COMX_ENABLE_BOARD_MEM | COMX_BOARD_RESET, 824 dev->base_addr); 825 826 len = 0; 827 COMX_address = (byte *)dev->mem_start; 828 while (fw->len > len) { 829 writeb(fw->data[len++], COMX_address++); 830 } 831 832 len = 0; 833 COMX_address = (byte *)dev->mem_start; 834 while (len != fw->len && readb(COMX_address++) == fw->data[len]) { 835 len++; 836 } 837 838 outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr); 839 840 if (len != fw->len) { 841 printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x " 842 "instead of 0x%02x\n", dev->name, len, 843 readb(COMX_address - 1), fw->data[len]); 844 retval=-EAGAIN; 845 goto out; 846 } 847 848 loopcount=0; 849 while( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) { 850 udelay(100); 851 } 852 853 if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) { 854 printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n", 855 dev->name, COMX_readw(dev, OFF_A_L2_LINKUP)); 856 retval=-EAGAIN; 857 goto out; 858 } 859 860 ch->init_status |= FW_LOADED; 861 retval=0; 862 863out: 864 outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr); 865 if(saved) { 866 ((struct comx_channel *)saved->priv)->HW_board_on(saved); 867 } 868 memory_used[mempos]=saved; 869 restore_flags(flags); 870 return retval; 871} 872 873static int HICOMX_load_board(struct net_device *dev) 874{ 875 struct comx_channel *ch = dev->priv; 876 struct comx_privdata *hw = ch->HW_privdata; 877 struct comx_firmware *fw = hw->firmware; 878 word board_segment = dev->mem_start >> 12; 879 int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16; 880 struct net_device *saved; 881 unsigned char id1, id2; 882 unsigned long flags; 883 int retval; 884 int loopcount; 885 int len; 886 word *HICOMX_address; 887 char id = 1; 888 889 if (!fw || !fw->len) { 890 struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL; 891 struct comx_privdata *twin_hw; 892 893 if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) { 894 return -EAGAIN; 895 } 896 897 if (!(fw = twin_hw->firmware) || !fw->len) { 898 return -EAGAIN; 899 } 900 } 901 902 while (id != 4) { 903 if (inb_p(dev->base_addr + id++) != HICOMX_ID_BYTE) { 904 break; 905 } 906 } 907 908 if (id != 4) { 909 printk(KERN_ERR "%s: can't find HICOMX at 0x%04x, id[%d] = %02x\n", 910 dev->name, (unsigned int)dev->base_addr, id - 1, 911 inb_p(dev->base_addr + id - 1)); 912 return -1; 913 } 914 915 id1 = fw->data[OFF_FW_L1_ID]; 916 id2 = fw->data[OFF_FW_L1_ID + 1]; 917 if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_HICOMX) { 918 printk(KERN_ERR "%s: incorrect firmware, load aborted\n", dev->name); 919 return -EAGAIN; 920 } 921 922 printk(KERN_INFO "%s: Loading HICOMX Layer 1 firmware %s\n", dev->name, 923 (char *)(fw->data + OFF_FW_L1_ID + 2)); 924 925 id1 = fw->data[OFF_FW_L2_ID]; 926 id2 = fw->data[OFF_FW_L2_ID + 1]; 927 if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) { 928 printk(KERN_INFO "with Layer 2 code %s\n", 929 (char *)(fw->data + OFF_FW_L2_ID + 2)); 930 } 931 932 outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr); 933 udelay(10); 934 935 save_flags(flags); cli(); 936 saved=memory_used[mempos]; 937 if(saved) { 938 ((struct comx_channel *)saved->priv)->HW_board_off(saved); 939 } 940 memory_used[mempos]=dev; 941 942 outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr); 943 outb_p(HICOMX_PRG_MEM, dev->base_addr + 1); 944 945 len = 0; 946 HICOMX_address = (word *)dev->mem_start; 947 while (fw->len > len) { 948 writeb(fw->data[len++], HICOMX_address++); 949 } 950 951 len = 0; 952 HICOMX_address = (word *)dev->mem_start; 953 while (len != fw->len && (readw(HICOMX_address++) & 0xff) == fw->data[len]) { 954 len++; 955 } 956 957 if (len != fw->len) { 958 printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x " 959 "instead of 0x%02x\n", dev->name, len, 960 readw(HICOMX_address - 1) & 0xff, fw->data[len]); 961 retval=-EAGAIN; 962 goto out; 963 } 964 965 outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr); 966 outb_p(HICOMX_DATA_MEM, dev->base_addr + 1); 967 968 outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr); 969 970 loopcount=0; 971 while(loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1) { 972 udelay(100); 973 } 974 975 if ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) { 976 printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n", 977 dev->name, COMX_readw(dev, OFF_A_L2_LINKUP)); 978 retval=-EAGAIN; 979 goto out; 980 } 981 982 ch->init_status |= FW_LOADED; 983 retval=0; 984 985out: 986 outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr); 987 outb_p(HICOMX_DATA_MEM, dev->base_addr + 1); 988 989 if(saved) { 990 ((struct comx_channel *)saved->priv)->HW_board_on(saved); 991 } 992 memory_used[mempos]=saved; 993 restore_flags(flags); 994 return retval; 995} 996 997static struct net_device *comx_twin_check(struct net_device *dev) 998{ 999 struct comx_channel *ch = dev->priv; 1000 struct proc_dir_entry *procfile = ch->procdir->parent->subdir; 1001 struct comx_privdata *hw = ch->HW_privdata; 1002 1003 struct net_device *twin; 1004 struct comx_channel *ch_twin; 1005 struct comx_privdata *hw_twin; 1006 1007 1008 for ( ; procfile ; procfile = procfile->next) { 1009 1010 if(!S_ISDIR(procfile->mode)) { 1011 continue; 1012 } 1013 1014 twin=procfile->data; 1015 ch_twin=twin->priv; 1016 hw_twin=ch_twin->HW_privdata; 1017 1018 1019 if (twin != dev && dev->irq && dev->base_addr && dev->mem_start && 1020 dev->irq == twin->irq && dev->base_addr == twin->base_addr && 1021 dev->mem_start == twin->mem_start && 1022 hw->channel == (1 - hw_twin->channel) && 1023 ch->hardware == ch_twin->hardware) { 1024 return twin; 1025 } 1026 } 1027 return NULL; 1028} 1029 1030static int comxhw_write_proc(struct file *file, const char *buffer, 1031 u_long count, void *data) 1032{ 1033 struct proc_dir_entry *entry = (struct proc_dir_entry *)data; 1034 struct net_device *dev = entry->parent->data; 1035 struct comx_channel *ch = dev->priv; 1036 struct comx_privdata *hw = ch->HW_privdata; 1037 char *page; 1038 1039 1040 if(ch->init_status & HW_OPEN) { 1041 return -EAGAIN; 1042 } 1043 1044 if (strcmp(FILENAME_FIRMWARE, entry->name) != 0) { 1045 if (!(page = (char *)__get_free_page(GFP_KERNEL))) { 1046 return -ENOMEM; 1047 } 1048 if(copy_from_user(page, buffer, count = (min_t(int, count, PAGE_SIZE)))) 1049 { 1050 count = -EFAULT; 1051 goto out; 1052 } 1053 if (page[count-1] == '\n') 1054 page[count-1] = '\0'; 1055 else if (count < PAGE_SIZE) 1056 page[count] = '\0'; 1057 else if (page[count]) { 1058 count = -EINVAL; 1059 goto out; 1060 } 1061 page[count]=0; /* Null terminate */ 1062 } else { 1063 byte *tmp; 1064 1065 if (!hw->firmware) { 1066 if ((hw->firmware = kmalloc(sizeof(struct comx_firmware), 1067 GFP_KERNEL)) == NULL) { 1068 return -ENOMEM; 1069 } 1070 hw->firmware->len = 0; 1071 hw->firmware->data = NULL; 1072 } 1073 1074 if ((tmp = kmalloc(count + file->f_pos, GFP_KERNEL)) == NULL) { 1075 return -ENOMEM; 1076 } 1077 1078 /* Ha nem 0 a fpos, akkor meglevo file-t irunk. Gyenge trukk. */ 1079 if (hw->firmware && hw->firmware->len && file->f_pos 1080 && hw->firmware->len < count + file->f_pos) { 1081 memcpy(tmp, hw->firmware->data, hw->firmware->len); 1082 } 1083 if (hw->firmware->data) { 1084 kfree(hw->firmware->data); 1085 } 1086 copy_from_user(tmp + file->f_pos, buffer, count); 1087 hw->firmware->len = entry->size = file->f_pos + count; 1088 hw->firmware->data = tmp; 1089 file->f_pos += count; 1090 return count; 1091 } 1092 1093 if (strcmp(entry->name, FILENAME_CHANNEL) == 0) { 1094 hw->channel = simple_strtoul(page, NULL, 0); 1095 if (hw->channel >= MAX_CHANNELNO) { 1096 printk(KERN_ERR "Invalid channel number\n"); 1097 hw->channel = 0; 1098 } 1099 if ((ch->twin = comx_twin_check(dev)) != NULL) { 1100 struct comx_channel *twin_ch = ch->twin->priv; 1101 twin_ch->twin = dev; 1102 } 1103 } else if (strcmp(entry->name, FILENAME_IRQ) == 0) { 1104 dev->irq = simple_strtoul(page, NULL, 0); 1105 if (dev->irq == 2) { 1106 dev->irq = 9; 1107 } 1108 if (dev->irq < 3 || dev->irq > 15) { 1109 printk(KERN_ERR "comxhw: Invalid irq number\n"); 1110 dev->irq = 0; 1111 } 1112 if ((ch->twin = comx_twin_check(dev)) != NULL) { 1113 struct comx_channel *twin_ch = ch->twin->priv; 1114 twin_ch->twin = dev; 1115 } 1116 } else if (strcmp(entry->name, FILENAME_IO) == 0) { 1117 dev->base_addr = simple_strtoul(page, NULL, 0); 1118 if ((dev->base_addr & 3) != 0 || dev->base_addr < 0x300 1119 || dev->base_addr > 0x3fc) { 1120 printk(KERN_ERR "Invalid io value\n"); 1121 dev->base_addr = 0; 1122 } 1123 if ((ch->twin = comx_twin_check(dev)) != NULL) { 1124 struct comx_channel *twin_ch = ch->twin->priv; 1125 1126 twin_ch->twin = dev; 1127 } 1128 } else if (strcmp(entry->name, FILENAME_MEMADDR) == 0) { 1129 dev->mem_start = simple_strtoul(page, NULL, 0); 1130 if (dev->mem_start <= 0xf000 && dev->mem_start >= 0xa000) { 1131 dev->mem_start *= 16; 1132 } 1133 if ((dev->mem_start & 0xfff) != 0 || dev->mem_start < COMX_MEM_MIN 1134 || dev->mem_start + hw->memory_size > COMX_MEM_MAX) { 1135 printk(KERN_ERR "Invalid memory page\n"); 1136 dev->mem_start = 0; 1137 } 1138 dev->mem_end = dev->mem_start + hw->memory_size; 1139 if ((ch->twin = comx_twin_check(dev)) != NULL) { 1140 struct comx_channel *twin_ch = ch->twin->priv; 1141 1142 twin_ch->twin = dev; 1143 } 1144 } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) { 1145 if (strncmp("ext", page, 3) == 0) { 1146 hw->clock = 0; 1147 } else { 1148 int kbps; 1149 1150 kbps = simple_strtoul(page, NULL, 0); 1151 hw->clock = kbps ? COMX_CLOCK_CONST/kbps : 0; 1152 } 1153 } 1154out: 1155 free_page((unsigned long)page); 1156 return count; 1157} 1158 1159static int comxhw_read_proc(char *page, char **start, off_t off, int count, 1160 int *eof, void *data) 1161{ 1162 struct proc_dir_entry *file = (struct proc_dir_entry *)data; 1163 struct net_device *dev = file->parent->data; 1164 struct comx_channel *ch = dev->priv; 1165 struct comx_privdata *hw = ch->HW_privdata; 1166 int len = 0; 1167 1168 1169 if (strcmp(file->name, FILENAME_IO) == 0) { 1170 len = sprintf(page, "0x%03x\n", (unsigned int)dev->base_addr); 1171 } else if (strcmp(file->name, FILENAME_IRQ) == 0) { 1172 len = sprintf(page, "0x%02x\n", dev->irq == 9 ? 2 : dev->irq); 1173 } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) { 1174 len = sprintf(page, "%01d\n", hw->channel); 1175 } else if (strcmp(file->name, FILENAME_MEMADDR) == 0) { 1176 len = sprintf(page, "0x%05x\n", (unsigned int)dev->mem_start); 1177 } else if (strcmp(file->name, FILENAME_TWIN) == 0) { 1178 len = sprintf(page, "%s\n", ch->twin ? ch->twin->name : "none"); 1179 } else if (strcmp(file->name, FILENAME_CLOCK) == 0) { 1180 if (hw->clock) { 1181 len = sprintf(page, "%-8d\n", COMX_CLOCK_CONST/hw->clock); 1182 } else { 1183 len = sprintf(page, "external\n"); 1184 } 1185 } else if (strcmp(file->name, FILENAME_FIRMWARE) == 0) { 1186 len = min_t(int, FILE_PAGESIZE, 1187 min_t(int, count, 1188 hw->firmware ? 1189 (hw->firmware->len - off) : 0)); 1190 if (len < 0) { 1191 len = 0; 1192 } 1193 *start = hw->firmware ? (hw->firmware->data + off) : NULL; 1194 if (off + len >= (hw->firmware ? hw->firmware->len : 0) || len == 0) { 1195 *eof = 1; 1196 } 1197 return len; 1198 } 1199 1200 if (off >= len) { 1201 *eof = 1; 1202 return 0; 1203 } 1204 1205 *start = page + off; 1206 if (count >= len - off) { 1207 *eof = 1; 1208 } 1209 return min_t(int, count, len - off); 1210} 1211 1212/* Called on echo comx >boardtype */ 1213static int COMX_init(struct net_device *dev) 1214{ 1215 struct comx_channel *ch = dev->priv; 1216 struct comx_privdata *hw; 1217 struct proc_dir_entry *new_file; 1218 1219 if ((ch->HW_privdata = kmalloc(sizeof(struct comx_privdata), 1220 GFP_KERNEL)) == NULL) { 1221 return -ENOMEM; 1222 } 1223 memset(hw = ch->HW_privdata, 0, sizeof(struct comx_privdata)); 1224 1225 if (ch->hardware == &comx_hw || ch->hardware == &cmx_hw) { 1226 hw->memory_size = COMX_MEMORY_SIZE; 1227 hw->io_extent = COMX_IO_EXTENT; 1228 dev->base_addr = COMX_DEFAULT_IO; 1229 dev->irq = COMX_DEFAULT_IRQ; 1230 dev->mem_start = COMX_DEFAULT_MEMADDR; 1231 dev->mem_end = COMX_DEFAULT_MEMADDR + COMX_MEMORY_SIZE; 1232 } else if (ch->hardware == &hicomx_hw) { 1233 hw->memory_size = HICOMX_MEMORY_SIZE; 1234 hw->io_extent = HICOMX_IO_EXTENT; 1235 dev->base_addr = HICOMX_DEFAULT_IO; 1236 dev->irq = HICOMX_DEFAULT_IRQ; 1237 dev->mem_start = HICOMX_DEFAULT_MEMADDR; 1238 dev->mem_end = HICOMX_DEFAULT_MEMADDR + HICOMX_MEMORY_SIZE; 1239 } else { 1240 printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__); 1241 } 1242 1243 if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, ch->procdir)) 1244 == NULL) { 1245 goto cleanup_HW_privdata; 1246 } 1247 new_file->data = (void *)new_file; 1248 new_file->read_proc = &comxhw_read_proc; 1249 new_file->write_proc = &comxhw_write_proc; 1250 new_file->size = 6; 1251 new_file->nlink = 1; 1252 1253 if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, ch->procdir)) 1254 == NULL) { 1255 goto cleanup_filename_io; 1256 } 1257 new_file->data = (void *)new_file; 1258 new_file->read_proc = &comxhw_read_proc; 1259 new_file->write_proc = &comxhw_write_proc; 1260 new_file->size = 5; 1261 new_file->nlink = 1; 1262 1263 if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644, 1264 ch->procdir)) == NULL) { 1265 goto cleanup_filename_irq; 1266 } 1267 new_file->data = (void *)new_file; 1268 new_file->read_proc = &comxhw_read_proc; 1269 new_file->write_proc = &comxhw_write_proc; 1270 new_file->size = 2; // Ezt tudjuk 1271 new_file->nlink = 1; 1272 1273 if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) { 1274 if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, 1275 ch->procdir)) == NULL) { 1276 goto cleanup_filename_channel; 1277 } 1278 new_file->data = (void *)new_file; 1279 new_file->read_proc = &comxhw_read_proc; 1280 new_file->write_proc = &comxhw_write_proc; 1281 new_file->size = 9; 1282 new_file->nlink = 1; 1283 } 1284 1285 if ((new_file = create_proc_entry(FILENAME_MEMADDR, S_IFREG | 0644, 1286 ch->procdir)) == NULL) { 1287 goto cleanup_filename_clock; 1288 } 1289 new_file->data = (void *)new_file; 1290 new_file->read_proc = &comxhw_read_proc; 1291 new_file->write_proc = &comxhw_write_proc; 1292 new_file->size = 8; 1293 new_file->nlink = 1; 1294 1295 if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, 1296 ch->procdir)) == NULL) { 1297 goto cleanup_filename_memaddr; 1298 } 1299 new_file->data = (void *)new_file; 1300 new_file->read_proc = &comxhw_read_proc; 1301 new_file->write_proc = NULL; 1302 new_file->nlink = 1; 1303 1304 if ((new_file = create_proc_entry(FILENAME_FIRMWARE, S_IFREG | 0644, 1305 ch->procdir)) == NULL) { 1306 goto cleanup_filename_twin; 1307 } 1308 new_file->data = (void *)new_file; 1309 new_file->read_proc = &comxhw_read_proc; 1310 new_file->write_proc = &comxhw_write_proc; 1311 new_file->nlink = 1; 1312 1313 if (ch->hardware == &comx_hw) { 1314 ch->HW_board_on = COMX_board_on; 1315 ch->HW_board_off = COMX_board_off; 1316 ch->HW_load_board = COMX_load_board; 1317 } else if (ch->hardware == &cmx_hw) { 1318 ch->HW_board_on = COMX_board_on; 1319 ch->HW_board_off = COMX_board_off; 1320 ch->HW_load_board = CMX_load_board; 1321 ch->HW_set_clock = COMX_set_clock; 1322 } else if (ch->hardware == &hicomx_hw) { 1323 ch->HW_board_on = HICOMX_board_on; 1324 ch->HW_board_off = HICOMX_board_off; 1325 ch->HW_load_board = HICOMX_load_board; 1326 ch->HW_set_clock = COMX_set_clock; 1327 } else { 1328 printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__); 1329 } 1330 1331 ch->HW_access_board = COMX_access_board; 1332 ch->HW_release_board = COMX_release_board; 1333 ch->HW_txe = COMX_txe; 1334 ch->HW_open = COMX_open; 1335 ch->HW_close = COMX_close; 1336 ch->HW_send_packet = COMX_send_packet; 1337 ch->HW_statistics = COMX_statistics; 1338 1339 if ((ch->twin = comx_twin_check(dev)) != NULL) { 1340 struct comx_channel *twin_ch = ch->twin->priv; 1341 1342 twin_ch->twin = dev; 1343 } 1344 1345 MOD_INC_USE_COUNT; 1346 return 0; 1347 1348cleanup_filename_twin: 1349 remove_proc_entry(FILENAME_TWIN, ch->procdir); 1350cleanup_filename_memaddr: 1351 remove_proc_entry(FILENAME_MEMADDR, ch->procdir); 1352cleanup_filename_clock: 1353 if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) 1354 remove_proc_entry(FILENAME_CLOCK, ch->procdir); 1355cleanup_filename_channel: 1356 remove_proc_entry(FILENAME_CHANNEL, ch->procdir); 1357cleanup_filename_irq: 1358 remove_proc_entry(FILENAME_IRQ, ch->procdir); 1359cleanup_filename_io: 1360 remove_proc_entry(FILENAME_IO, ch->procdir); 1361cleanup_HW_privdata: 1362 kfree(ch->HW_privdata); 1363 return -EIO; 1364} 1365 1366/* Called on echo valami >boardtype */ 1367static int COMX_exit(struct net_device *dev) 1368{ 1369 struct comx_channel *ch = dev->priv; 1370 struct comx_privdata *hw = ch->HW_privdata; 1371 1372 if (hw->firmware) { 1373 if (hw->firmware->data) kfree(hw->firmware->data); 1374 kfree(hw->firmware); 1375 } if (ch->twin) { 1376 struct comx_channel *twin_ch = ch->twin->priv; 1377 1378 twin_ch->twin = NULL; 1379 } 1380 1381 kfree(ch->HW_privdata); 1382 remove_proc_entry(FILENAME_IO, ch->procdir); 1383 remove_proc_entry(FILENAME_IRQ, ch->procdir); 1384 remove_proc_entry(FILENAME_CHANNEL, ch->procdir); 1385 remove_proc_entry(FILENAME_MEMADDR, ch->procdir); 1386 remove_proc_entry(FILENAME_FIRMWARE, ch->procdir); 1387 remove_proc_entry(FILENAME_TWIN, ch->procdir); 1388 if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) { 1389 remove_proc_entry(FILENAME_CLOCK, ch->procdir); 1390 } 1391 1392 MOD_DEC_USE_COUNT; 1393 return 0; 1394} 1395 1396static int COMX_dump(struct net_device *dev) 1397{ 1398 printk(KERN_INFO "%s: COMX_dump called, why ?\n", dev->name); 1399 return 0; 1400} 1401 1402static struct comx_hardware comx_hw = { 1403 "comx", 1404 VERSION, 1405 COMX_init, 1406 COMX_exit, 1407 COMX_dump, 1408 NULL 1409}; 1410 1411static struct comx_hardware cmx_hw = { 1412 "cmx", 1413 VERSION, 1414 COMX_init, 1415 COMX_exit, 1416 COMX_dump, 1417 NULL 1418}; 1419 1420static struct comx_hardware hicomx_hw = { 1421 "hicomx", 1422 VERSION, 1423 COMX_init, 1424 COMX_exit, 1425 COMX_dump, 1426 NULL 1427}; 1428 1429#ifdef MODULE 1430#define comx_hw_comx_init init_module 1431#endif 1432 1433int __init comx_hw_comx_init(void) 1434{ 1435 comx_register_hardware(&comx_hw); 1436 comx_register_hardware(&cmx_hw); 1437 comx_register_hardware(&hicomx_hw); 1438 memset(memory_used, 0, sizeof(memory_used)); 1439 return 0; 1440} 1441 1442#ifdef MODULE 1443void cleanup_module(void) 1444{ 1445 comx_unregister_hardware("comx"); 1446 comx_unregister_hardware("cmx"); 1447 comx_unregister_hardware("hicomx"); 1448} 1449#endif 1450