2 * Hardware-level driver for the COMX and HICOMX cards
3 * for Linux kernel 2.2.X
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>
10 * Copyright (C) 1995-2000 ITConsult-Pro Co. <info@itc.hu>
13 * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 0.86
14 * Daniele Bellucci <bellucda@tiscali.it> - 0.87
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version
19 * 2 of the License, or (at your option) any later version.
21 * Version 0.80 (99/06/11):
22 * - port back to kernel, add support builtin driver
23 * - cleaned up the source code a bit
25 * Version 0.81 (99/06/22):
26 * - cleaned up the board load functions, no more long reset
28 * - lower modem lines on close
29 * - some interrupt handling fixes
31 * Version 0.82 (99/08/24):
32 * - fix multiple board support
34 * Version 0.83 (99/11/30):
35 * - interrupt handling and locking fixes during initalization
36 * - really fix multiple board support
38 * Version 0.84 (99/12/02):
39 * - some workarounds for problematic hardware/firmware
41 * Version 0.85 (00/01/14):
42 * - some additional workarounds :/
44 * Version 0.86 (00/08/15):
45 * - resource release on failure at COMX_init
47 * Version 0.87 (03/07/09)
48 * - audit copy_from_user in comxhw_write_proc
51 #define VERSION "0.87"
53 #include <linux/module.h>
54 #include <linux/types.h>
55 #include <linux/netdevice.h>
56 #include <linux/proc_fs.h>
57 #include <linux/ioport.h>
58 #include <linux/init.h>
59 #include <linux/delay.h>
61 #include <asm/uaccess.h>
67 MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>, Tivadar Szemethy <tiv@itc.hu>, Arpad Bakay");
68 MODULE_DESCRIPTION("Hardware-level driver for the COMX and HICOMX adapters\n");
69 MODULE_LICENSE("GPL");
71 #define COMX_readw(dev, offset) (readw(dev->mem_start + offset + \
72 (unsigned int)(((struct comx_privdata *)\
73 ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \
74 * COMX_CHANNEL_OFFSET))
76 #define COMX_WRITE(dev, offset, value) (writew(value, dev->mem_start + offset \
77 + (unsigned int)(((struct comx_privdata *) \
78 ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \
79 * COMX_CHANNEL_OFFSET))
81 #define COMX_CMD(dev, cmd) (COMX_WRITE(dev, OFF_A_L2_CMD, cmd))
83 struct comx_firmware {
88 struct comx_privdata {
89 struct comx_firmware *firmware;
91 char channel; // channel no.
97 static struct net_device *memory_used[(COMX_MEM_MAX - COMX_MEM_MIN) / 0x10000];
98 extern struct comx_hardware hicomx_hw;
99 extern struct comx_hardware comx_hw;
100 extern struct comx_hardware cmx_hw;
102 static irqreturn_t COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs);
104 static void COMX_board_on(struct net_device *dev)
106 outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) |
107 COMX_ENABLE_BOARD_IT | COMX_ENABLE_BOARD_MEM), dev->base_addr);
110 static void COMX_board_off(struct net_device *dev)
112 outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) |
113 COMX_ENABLE_BOARD_IT), dev->base_addr);
116 static void HICOMX_board_on(struct net_device *dev)
118 outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) |
119 HICOMX_ENABLE_BOARD_MEM), dev->base_addr);
122 static void HICOMX_board_off(struct net_device *dev)
124 outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) |
125 HICOMX_DISABLE_BOARD_MEM), dev->base_addr);
128 static void COMX_set_clock(struct net_device *dev)
130 struct comx_channel *ch = dev->priv;
131 struct comx_privdata *hw = ch->HW_privdata;
133 COMX_WRITE(dev, OFF_A_L1_CLKINI, hw->clock);
136 static struct net_device *COMX_access_board(struct net_device *dev)
138 struct comx_channel *ch = dev->priv;
139 struct net_device *ret;
140 int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
144 save_flags(flags); cli();
146 ret = memory_used[mempos];
152 memory_used[mempos] = dev;
154 if (!ch->twin || ret != ch->twin) {
155 if (ret) ((struct comx_channel *)ret->priv)->HW_board_off(ret);
156 ch->HW_board_on(dev);
159 restore_flags(flags);
163 static void COMX_release_board(struct net_device *dev, struct net_device *savep)
166 int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
167 struct comx_channel *ch = dev->priv;
169 save_flags(flags); cli();
171 if (memory_used[mempos] == savep) {
175 memory_used[mempos] = savep;
176 if (!ch->twin || ch->twin != savep) {
177 ch->HW_board_off(dev);
178 if (savep) ((struct comx_channel*)savep->priv)->HW_board_on(savep);
181 restore_flags(flags);
184 static int COMX_txe(struct net_device *dev)
186 struct net_device *savep;
187 struct comx_channel *ch = dev->priv;
190 savep = ch->HW_access_board(dev);
191 if (COMX_readw(dev,OFF_A_L2_LINKUP) == LINKUP_READY) {
192 rc = COMX_readw(dev,OFF_A_L2_TxEMPTY);
194 ch->HW_release_board(dev,savep);
196 printk(KERN_ERR "%s, OFF_A_L2_TxEMPTY is %d\n",dev->name, rc);
201 static int COMX_send_packet(struct net_device *dev, struct sk_buff *skb)
203 struct net_device *savep;
204 struct comx_channel *ch = dev->priv;
205 struct comx_privdata *hw = ch->HW_privdata;
206 int ret = FRAME_DROPPED;
209 savep = ch->HW_access_board(dev);
211 if (ch->debug_flags & DEBUG_HW_TX) {
212 comx_debug_bytes(dev, skb->data, skb->len,"COMX_send packet");
215 if (skb->len > COMX_MAX_TX_SIZE) {
220 tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY);
221 if ((ch->line_status & LINE_UP) && tmp==1) {
222 int lensave = skb->len;
223 int dest = COMX_readw(dev, OFF_A_L2_TxBUFP);
224 word *data = (word *)skb->data;
227 printk(KERN_ERR "%s: OFF_A_L2_TxBUFP is %d\n", dev->name, dest);
232 writew((unsigned short)skb->len, dev->mem_start + dest);
234 while (skb->len > 1) {
235 writew(*data++, dev->mem_start + dest);
236 dest += 2; skb->len -= 2;
239 writew(*((byte *)data), dev->mem_start + dest);
241 writew(0, dev->mem_start + (int)hw->channel *
242 COMX_CHANNEL_OFFSET + OFF_A_L2_TxEMPTY);
243 ch->stats.tx_packets++;
244 ch->stats.tx_bytes += lensave;
245 ret = FRAME_ACCEPTED;
247 ch->stats.tx_dropped++;
248 printk(KERN_INFO "%s: frame dropped\n",dev->name);
250 printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n",dev->name,tmp);
255 ch->HW_release_board(dev, savep);
260 static inline int comx_read_buffer(struct net_device *dev)
262 struct comx_channel *ch = dev->priv;
270 rbuf_offs = COMX_readw(dev, OFF_A_L2_RxBUFP);
271 if(rbuf_offs == 0xffff) {
272 printk(KERN_ERR "%s: OFF_A_L2_RxBUFP is %d\n",dev->name,rbuf_offs);
275 len = readw(dev->mem_start + rbuf_offs);
276 if(len > COMX_MAX_RX_SIZE) {
277 printk(KERN_ERR "%s: packet length is %d\n",dev->name,len);
280 if ((skb = dev_alloc_skb(len + 16)) == NULL) {
281 ch->stats.rx_dropped++;
282 COMX_WRITE(dev, OFF_A_L2_DAV, 0);
286 skb_reserve(skb, 16);
289 writeptr = (word *)skb->data;
291 *writeptr++ = readw(dev->mem_start + rbuf_offs);
295 COMX_WRITE(dev, OFF_A_L2_DAV, 0);
296 ch->stats.rx_packets++;
297 ch->stats.rx_bytes += len;
298 if (ch->debug_flags & DEBUG_HW_RX) {
299 comx_debug_skb(dev, skb, "COMX_interrupt receiving");
301 ch->LINE_rx(dev, skb);
305 static inline char comx_line_change(struct net_device *dev, char linestat)
307 struct comx_channel *ch=dev->priv;
311 if (linestat & LINE_UP) { /* Vonal fol */
312 if (ch->lineup_delay) {
313 if (!test_and_set_bit(0, &ch->lineup_pending)) {
314 ch->lineup_timer.function = comx_lineup_func;
315 ch->lineup_timer.data = (unsigned long)dev;
316 ch->lineup_timer.expires = jiffies +
318 add_timer(&ch->lineup_timer);
323 ch->LINE_status(dev, ch->line_status |= LINE_UP);
325 } else { /* Vonal le */
327 if (test_and_clear_bit(0, &ch->lineup_pending)) {
328 del_timer(&ch->lineup_timer);
330 ch->line_status &= ~LINE_UP;
331 if (ch->LINE_status) {
332 ch->LINE_status(dev, ch->line_status);
341 static irqreturn_t COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs)
343 struct net_device *dev = dev_id;
344 struct comx_channel *ch = dev->priv;
345 struct comx_privdata *hw = ch->HW_privdata;
346 struct net_device *interrupted;
353 printk(KERN_ERR "COMX_interrupt: irq %d for unknown device\n", irq);
359 interrupted = ch->HW_access_board(dev);
361 while (!idle && count < 5000) {
365 while (channel < 2) {
367 char buffers_emptied = 0;
373 hw = ch->HW_privdata;
378 COMX_WRITE(dev, OFF_A_L1_REPENA,
379 COMX_readw(dev, OFF_A_L1_REPENA) & 0xFF00);
383 if ((ch->init_status & (HW_OPEN | LINE_OPEN)) !=
384 (HW_OPEN | LINE_OPEN)) {
389 tmp = COMX_readw(dev, OFF_A_L1_ABOREC);
390 COMX_WRITE(dev, OFF_A_L1_ABOREC, 0);
392 printk(KERN_ERR "%s: OFF_A_L1_ABOREC is %d\n",dev->name,tmp);
395 ch->stats.rx_missed_errors += (tmp >> 8) & 0xff;
396 ch->stats.rx_over_errors += tmp & 0xff;
398 tmp = COMX_readw(dev, OFF_A_L1_CRCREC);
399 COMX_WRITE(dev, OFF_A_L1_CRCREC, 0);
401 printk(KERN_ERR "%s: OFF_A_L1_CRCREC is %d\n",dev->name,tmp);
404 ch->stats.rx_crc_errors += (tmp >> 8) & 0xff;
405 ch->stats.rx_missed_errors += tmp & 0xff;
408 if ((ch->line_status & LINE_UP) && ch->LINE_rx) {
409 tmp=COMX_readw(dev, OFF_A_L2_DAV);
412 buffers_emptied+=comx_read_buffer(dev);
413 tmp=COMX_readw(dev, OFF_A_L2_DAV);
416 printk(KERN_ERR "%s: OFF_A_L2_DAV is %d\n", dev->name, tmp);
421 tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY);
422 if (tmp==1 && ch->LINE_tx) {
426 printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n", dev->name, tmp);
430 if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
431 linestat &= ~LINE_UP;
436 if ((linestat & LINE_UP) != (ch->line_status & LINE_UP)) {
437 ch->stats.tx_carrier_errors++;
438 idle &= comx_line_change(dev,linestat);
441 hw->histogram[(int)buffers_emptied]++;
447 printk(KERN_WARNING "%s: interrupt stuck\n",dev->name);
450 ch->HW_release_board(dev, interrupted);
454 static int COMX_open(struct net_device *dev)
456 struct comx_channel *ch = dev->priv;
457 struct comx_privdata *hw = ch->HW_privdata;
458 struct proc_dir_entry *procfile = ch->procdir->subdir;
462 struct net_device *savep;
464 if (!dev->base_addr || !dev->irq || !dev->mem_start) {
468 if (ch->twin && (((struct comx_channel *)(ch->twin->priv))->init_status & HW_OPEN)) {
473 if (!request_region(dev->base_addr, hw->io_extent, dev->name)) {
476 if (request_irq(dev->irq, COMX_interrupt, 0, dev->name,
478 printk(KERN_ERR "comx-hw-comx: unable to obtain irq %d\n", dev->irq);
479 release_region(dev->base_addr, hw->io_extent);
482 ch->init_status |= IRQ_ALLOCATED;
483 if (!ch->HW_load_board || ch->HW_load_board(dev)) {
484 ch->init_status &= ~IRQ_ALLOCATED;
490 savep = ch->HW_access_board(dev);
491 COMX_WRITE(dev, OFF_A_L2_LINKUP, 0);
493 if (ch->HW_set_clock) {
494 ch->HW_set_clock(dev);
497 COMX_CMD(dev, COMX_CMD_INIT);
499 while (COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && time_before(jiffies, jiffs + HZ)) {
503 if (time_after_eq(jiffies, jiffs + HZ)) {
504 printk(KERN_ERR "%s: board timeout on INIT command\n", dev->name);
505 ch->HW_release_board(dev, savep);
511 COMX_CMD(dev, COMX_CMD_OPEN);
514 while (COMX_readw(dev, OFF_A_L2_LINKUP) != 3 && time_before(jiffies, jiffs + HZ)) {
518 if (time_after_eq(jiffies, jiffs + HZ)) {
519 printk(KERN_ERR "%s: board timeout on OPEN command\n", dev->name);
520 ch->HW_release_board(dev, savep);
525 ch->init_status |= HW_OPEN;
527 /* Ez eleg ciki, de ilyen a rendszer */
528 if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
529 ch->line_status &= ~LINE_UP;
531 ch->line_status |= LINE_UP;
534 if (ch->LINE_status) {
535 ch->LINE_status(dev, ch->line_status);
538 ch->HW_release_board(dev, savep);
540 for ( ; procfile ; procfile = procfile->next) {
541 if (strcmp(procfile->name, FILENAME_IRQ) == 0
542 || strcmp(procfile->name, FILENAME_IO) == 0
543 || strcmp(procfile->name, FILENAME_MEMADDR) == 0
544 || strcmp(procfile->name, FILENAME_CHANNEL) == 0
545 || strcmp(procfile->name, FILENAME_FIRMWARE) == 0
546 || strcmp(procfile->name, FILENAME_CLOCK) == 0) {
547 procfile->mode = S_IFREG | 0444;
556 release_region(dev->base_addr, hw->io_extent);
557 free_irq(dev->irq, (void *)dev);
563 static int COMX_close(struct net_device *dev)
565 struct comx_channel *ch = dev->priv;
566 struct proc_dir_entry *procfile = ch->procdir->subdir;
567 struct comx_privdata *hw = ch->HW_privdata;
568 struct comx_channel *twin_ch;
569 struct net_device *savep;
571 savep = ch->HW_access_board(dev);
573 COMX_CMD(dev, COMX_CMD_CLOSE);
575 COMX_CMD(dev, COMX_CMD_EXIT);
577 ch->HW_release_board(dev, savep);
579 if (ch->init_status & IRQ_ALLOCATED) {
580 free_irq(dev->irq, (void *)dev);
581 ch->init_status &= ~IRQ_ALLOCATED;
583 release_region(dev->base_addr, hw->io_extent);
585 if (ch->twin && (twin_ch = ch->twin->priv) &&
586 (twin_ch->init_status & HW_OPEN)) {
587 /* Pass the irq to the twin */
588 if (request_irq(dev->irq, COMX_interrupt, 0, ch->twin->name,
589 (void *)ch->twin) == 0) {
590 twin_ch->init_status |= IRQ_ALLOCATED;
594 for ( ; procfile ; procfile = procfile->next) {
595 if (strcmp(procfile->name, FILENAME_IRQ) == 0
596 || strcmp(procfile->name, FILENAME_IO) == 0
597 || strcmp(procfile->name, FILENAME_MEMADDR) == 0
598 || strcmp(procfile->name, FILENAME_CHANNEL) == 0
599 || strcmp(procfile->name, FILENAME_FIRMWARE) == 0
600 || strcmp(procfile->name, FILENAME_CLOCK) == 0) {
601 procfile->mode = S_IFREG | 0644;
605 ch->init_status &= ~HW_OPEN;
609 static int COMX_statistics(struct net_device *dev, char *page)
611 struct comx_channel *ch = dev->priv;
612 struct comx_privdata *hw = ch->HW_privdata;
613 struct net_device *savep;
616 savep = ch->HW_access_board(dev);
618 len += sprintf(page + len, "Board data: %s %s %s %s\nPBUFOVR: %02x, "
619 "MODSTAT: %02x, LINKUP: %02x, DAV: %02x\nRxBUFP: %02x, "
620 "TxEMPTY: %02x, TxBUFP: %02x\n",
621 (ch->init_status & HW_OPEN) ? "HW_OPEN" : "",
622 (ch->init_status & LINE_OPEN) ? "LINE_OPEN" : "",
623 (ch->init_status & FW_LOADED) ? "FW_LOADED" : "",
624 (ch->init_status & IRQ_ALLOCATED) ? "IRQ_ALLOCATED" : "",
625 COMX_readw(dev, OFF_A_L1_PBUFOVR) & 0xff,
626 (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) & 0xff,
627 COMX_readw(dev, OFF_A_L2_LINKUP) & 0xff,
628 COMX_readw(dev, OFF_A_L2_DAV) & 0xff,
629 COMX_readw(dev, OFF_A_L2_RxBUFP) & 0xff,
630 COMX_readw(dev, OFF_A_L2_TxEMPTY) & 0xff,
631 COMX_readw(dev, OFF_A_L2_TxBUFP) & 0xff);
633 len += sprintf(page + len, "hist[0]: %8lu hist[1]: %8lu hist[2]: %8lu\n"
634 "hist[3]: %8lu hist[4]: %8lu\n",hw->histogram[0],hw->histogram[1],
635 hw->histogram[2],hw->histogram[3],hw->histogram[4]);
637 ch->HW_release_board(dev, savep);
642 static int COMX_load_board(struct net_device *dev)
644 struct comx_channel *ch = dev->priv;
645 struct comx_privdata *hw = ch->HW_privdata;
646 struct comx_firmware *fw = hw->firmware;
647 word board_segment = dev->mem_start >> 16;
648 int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
650 unsigned char id1, id2;
651 struct net_device *saved;
657 if (!fw || !fw->len) {
658 struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
659 struct comx_privdata *twin_hw;
661 if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
665 if (!(fw = twin_hw->firmware) || !fw->len) {
670 id1 = fw->data[OFF_FW_L1_ID];
671 id2 = fw->data[OFF_FW_L1_ID + 1];
673 if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_COMX) {
674 printk(KERN_ERR "%s: incorrect firmware, load aborted\n",
679 printk(KERN_INFO "%s: Loading COMX Layer 1 firmware %s\n", dev->name,
680 (char *)(fw->data + OFF_FW_L1_ID + 2));
682 id1 = fw->data[OFF_FW_L2_ID];
683 id2 = fw->data[OFF_FW_L2_ID + 1];
684 if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) {
685 printk(KERN_INFO "with Layer 2 code %s\n",
686 (char *)(fw->data + OFF_FW_L2_ID + 2));
689 outb_p(board_segment | COMX_BOARD_RESET, dev->base_addr);
690 /* 10 usec should be enough here */
693 save_flags(flags); cli();
694 saved=memory_used[mempos];
696 ((struct comx_channel *)saved->priv)->HW_board_off(saved);
698 memory_used[mempos]=dev;
700 outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
702 writeb(0, dev->mem_start + COMX_JAIL_OFFSET);
705 while(loopcount++ < 10000 &&
706 readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) {
710 if (readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) {
711 printk(KERN_ERR "%s: Can't reset board, JAIL value is %02x\n",
712 dev->name, readb(dev->mem_start + COMX_JAIL_OFFSET));
717 writeb(0x55, dev->mem_start + 0x18ff);
720 while(loopcount++ < 10000 && readb(dev->mem_start + 0x18ff) != 0) {
724 if(readb(dev->mem_start + 0x18ff) != 0) {
725 printk(KERN_ERR "%s: Can't reset board, reset timeout\n",
732 COMX_address = (byte *)dev->mem_start;
733 while (fw->len > len) {
734 writeb(fw->data[len++], COMX_address++);
738 COMX_address = (byte *)dev->mem_start;
739 while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
743 if (len != fw->len) {
744 printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
745 "instead of 0x%02x\n", dev->name, len,
746 readb(COMX_address - 1), fw->data[len]);
751 writeb(0, dev->mem_start + COMX_JAIL_OFFSET);
754 while ( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
758 if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
759 printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
760 dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
766 ch->init_status |= FW_LOADED;
770 outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
772 ((struct comx_channel *)saved->priv)->HW_board_on(saved);
774 memory_used[mempos]=saved;
775 restore_flags(flags);
779 static int CMX_load_board(struct net_device *dev)
781 struct comx_channel *ch = dev->priv;
782 struct comx_privdata *hw = ch->HW_privdata;
783 struct comx_firmware *fw = hw->firmware;
784 word board_segment = dev->mem_start >> 16;
785 int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
787 unsigned char id1, id2;
789 struct net_device *saved;
796 if (!fw || !fw->len) {
797 struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
798 struct comx_privdata *twin_hw;
800 if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
804 if (!(fw = twin_hw->firmware) || !fw->len) {
809 /* Ide kell olyat tenni, hogy ellenorizze az ID-t */
811 if (inb_p(dev->base_addr) != CMX_ID_BYTE) {
812 printk(KERN_ERR "%s: CMX id byte is invalid(%02x)\n", dev->name,
813 inb_p(dev->base_addr));
817 printk(KERN_INFO "%s: Loading CMX Layer 1 firmware %s\n", dev->name,
818 (char *)(fw->data + OFF_FW_L1_ID + 2));
820 save_flags(flags); cli();
821 saved=memory_used[mempos];
823 ((struct comx_channel *)saved->priv)->HW_board_off(saved);
825 memory_used[mempos]=dev;
827 outb_p(board_segment | COMX_ENABLE_BOARD_MEM | COMX_BOARD_RESET,
831 COMX_address = (byte *)dev->mem_start;
832 while (fw->len > len) {
833 writeb(fw->data[len++], COMX_address++);
837 COMX_address = (byte *)dev->mem_start;
838 while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
842 outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
844 if (len != fw->len) {
845 printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
846 "instead of 0x%02x\n", dev->name, len,
847 readb(COMX_address - 1), fw->data[len]);
853 while( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
857 if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
858 printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
859 dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
864 ch->init_status |= FW_LOADED;
868 outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
870 ((struct comx_channel *)saved->priv)->HW_board_on(saved);
872 memory_used[mempos]=saved;
873 restore_flags(flags);
877 static int HICOMX_load_board(struct net_device *dev)
879 struct comx_channel *ch = dev->priv;
880 struct comx_privdata *hw = ch->HW_privdata;
881 struct comx_firmware *fw = hw->firmware;
882 word board_segment = dev->mem_start >> 12;
883 int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
884 struct net_device *saved;
885 unsigned char id1, id2;
890 word *HICOMX_address;
893 if (!fw || !fw->len) {
894 struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
895 struct comx_privdata *twin_hw;
897 if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
901 if (!(fw = twin_hw->firmware) || !fw->len) {
907 if (inb_p(dev->base_addr + id++) != HICOMX_ID_BYTE) {
913 printk(KERN_ERR "%s: can't find HICOMX at 0x%04x, id[%d] = %02x\n",
914 dev->name, (unsigned int)dev->base_addr, id - 1,
915 inb_p(dev->base_addr + id - 1));
919 id1 = fw->data[OFF_FW_L1_ID];
920 id2 = fw->data[OFF_FW_L1_ID + 1];
921 if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_HICOMX) {
922 printk(KERN_ERR "%s: incorrect firmware, load aborted\n", dev->name);
926 printk(KERN_INFO "%s: Loading HICOMX Layer 1 firmware %s\n", dev->name,
927 (char *)(fw->data + OFF_FW_L1_ID + 2));
929 id1 = fw->data[OFF_FW_L2_ID];
930 id2 = fw->data[OFF_FW_L2_ID + 1];
931 if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) {
932 printk(KERN_INFO "with Layer 2 code %s\n",
933 (char *)(fw->data + OFF_FW_L2_ID + 2));
936 outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
939 save_flags(flags); cli();
940 saved=memory_used[mempos];
942 ((struct comx_channel *)saved->priv)->HW_board_off(saved);
944 memory_used[mempos]=dev;
946 outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
947 outb_p(HICOMX_PRG_MEM, dev->base_addr + 1);
950 HICOMX_address = (word *)dev->mem_start;
951 while (fw->len > len) {
952 writeb(fw->data[len++], HICOMX_address++);
956 HICOMX_address = (word *)dev->mem_start;
957 while (len != fw->len && (readw(HICOMX_address++) & 0xff) == fw->data[len]) {
961 if (len != fw->len) {
962 printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
963 "instead of 0x%02x\n", dev->name, len,
964 readw(HICOMX_address - 1) & 0xff, fw->data[len]);
969 outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
970 outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
972 outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
975 while(loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
979 if ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
980 printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
981 dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
986 ch->init_status |= FW_LOADED;
990 outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr);
991 outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
994 ((struct comx_channel *)saved->priv)->HW_board_on(saved);
996 memory_used[mempos]=saved;
997 restore_flags(flags);
1001 static struct net_device *comx_twin_check(struct net_device *dev)
1003 struct comx_channel *ch = dev->priv;
1004 struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
1005 struct comx_privdata *hw = ch->HW_privdata;
1007 struct net_device *twin;
1008 struct comx_channel *ch_twin;
1009 struct comx_privdata *hw_twin;
1012 for ( ; procfile ; procfile = procfile->next) {
1014 if(!S_ISDIR(procfile->mode)) {
1018 twin=procfile->data;
1020 hw_twin=ch_twin->HW_privdata;
1023 if (twin != dev && dev->irq && dev->base_addr && dev->mem_start &&
1024 dev->irq == twin->irq && dev->base_addr == twin->base_addr &&
1025 dev->mem_start == twin->mem_start &&
1026 hw->channel == (1 - hw_twin->channel) &&
1027 ch->hardware == ch_twin->hardware) {
1034 static int comxhw_write_proc(struct file *file, const char *buffer,
1035 u_long count, void *data)
1037 struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
1038 struct net_device *dev = entry->parent->data;
1039 struct comx_channel *ch = dev->priv;
1040 struct comx_privdata *hw = ch->HW_privdata;
1044 if(ch->init_status & HW_OPEN) {
1048 if (strcmp(FILENAME_FIRMWARE, entry->name) != 0) {
1049 if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
1052 if(copy_from_user(page, buffer, count = (min_t(int, count, PAGE_SIZE))))
1057 if (page[count-1] == '\n')
1058 page[count-1] = '\0';
1059 else if (count < PAGE_SIZE)
1061 else if (page[count]) {
1065 page[count]=0; /* Null terminate */
1069 if (!hw->firmware) {
1070 if ((hw->firmware = kmalloc(sizeof(struct comx_firmware),
1071 GFP_KERNEL)) == NULL) {
1074 hw->firmware->len = 0;
1075 hw->firmware->data = NULL;
1078 if ((tmp = kmalloc(count + file->f_pos, GFP_KERNEL)) == NULL) {
1082 /* Ha nem 0 a fpos, akkor meglevo file-t irunk. Gyenge trukk. */
1083 if (hw->firmware && hw->firmware->len && file->f_pos
1084 && hw->firmware->len < count + file->f_pos) {
1085 memcpy(tmp, hw->firmware->data, hw->firmware->len);
1087 if (hw->firmware->data) {
1088 kfree(hw->firmware->data);
1090 if (copy_from_user(tmp + file->f_pos, buffer, count))
1092 hw->firmware->len = entry->size = file->f_pos + count;
1093 hw->firmware->data = tmp;
1094 file->f_pos += count;
1098 if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
1099 hw->channel = simple_strtoul(page, NULL, 0);
1100 if (hw->channel >= MAX_CHANNELNO) {
1101 printk(KERN_ERR "Invalid channel number\n");
1104 if ((ch->twin = comx_twin_check(dev)) != NULL) {
1105 struct comx_channel *twin_ch = ch->twin->priv;
1106 twin_ch->twin = dev;
1108 } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
1109 dev->irq = simple_strtoul(page, NULL, 0);
1110 if (dev->irq == 2) {
1113 if (dev->irq < 3 || dev->irq > 15) {
1114 printk(KERN_ERR "comxhw: Invalid irq number\n");
1117 if ((ch->twin = comx_twin_check(dev)) != NULL) {
1118 struct comx_channel *twin_ch = ch->twin->priv;
1119 twin_ch->twin = dev;
1121 } else if (strcmp(entry->name, FILENAME_IO) == 0) {
1122 dev->base_addr = simple_strtoul(page, NULL, 0);
1123 if ((dev->base_addr & 3) != 0 || dev->base_addr < 0x300
1124 || dev->base_addr > 0x3fc) {
1125 printk(KERN_ERR "Invalid io value\n");
1128 if ((ch->twin = comx_twin_check(dev)) != NULL) {
1129 struct comx_channel *twin_ch = ch->twin->priv;
1131 twin_ch->twin = dev;
1133 } else if (strcmp(entry->name, FILENAME_MEMADDR) == 0) {
1134 dev->mem_start = simple_strtoul(page, NULL, 0);
1135 if (dev->mem_start <= 0xf000 && dev->mem_start >= 0xa000) {
1136 dev->mem_start *= 16;
1138 if ((dev->mem_start & 0xfff) != 0 || dev->mem_start < COMX_MEM_MIN
1139 || dev->mem_start + hw->memory_size > COMX_MEM_MAX) {
1140 printk(KERN_ERR "Invalid memory page\n");
1143 dev->mem_end = dev->mem_start + hw->memory_size;
1144 if ((ch->twin = comx_twin_check(dev)) != NULL) {
1145 struct comx_channel *twin_ch = ch->twin->priv;
1147 twin_ch->twin = dev;
1149 } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
1150 if (strncmp("ext", page, 3) == 0) {
1155 kbps = simple_strtoul(page, NULL, 0);
1156 hw->clock = kbps ? COMX_CLOCK_CONST/kbps : 0;
1160 free_page((unsigned long)page);
1164 static int comxhw_read_proc(char *page, char **start, off_t off, int count,
1165 int *eof, void *data)
1167 struct proc_dir_entry *file = (struct proc_dir_entry *)data;
1168 struct net_device *dev = file->parent->data;
1169 struct comx_channel *ch = dev->priv;
1170 struct comx_privdata *hw = ch->HW_privdata;
1174 if (strcmp(file->name, FILENAME_IO) == 0) {
1175 len = sprintf(page, "0x%03x\n", (unsigned int)dev->base_addr);
1176 } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
1177 len = sprintf(page, "0x%02x\n", dev->irq == 9 ? 2 : dev->irq);
1178 } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
1179 len = sprintf(page, "%01d\n", hw->channel);
1180 } else if (strcmp(file->name, FILENAME_MEMADDR) == 0) {
1181 len = sprintf(page, "0x%05x\n", (unsigned int)dev->mem_start);
1182 } else if (strcmp(file->name, FILENAME_TWIN) == 0) {
1183 len = sprintf(page, "%s\n", ch->twin ? ch->twin->name : "none");
1184 } else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
1186 len = sprintf(page, "%-8d\n", COMX_CLOCK_CONST/hw->clock);
1188 len = sprintf(page, "external\n");
1190 } else if (strcmp(file->name, FILENAME_FIRMWARE) == 0) {
1191 len = min_t(int, FILE_PAGESIZE,
1194 (hw->firmware->len - off) : 0));
1198 *start = hw->firmware ? (hw->firmware->data + off) : NULL;
1199 if (off + len >= (hw->firmware ? hw->firmware->len : 0) || len == 0) {
1210 *start = page + off;
1211 if (count >= len - off) {
1214 return min_t(int, count, len - off);
1217 /* Called on echo comx >boardtype */
1218 static int COMX_init(struct net_device *dev)
1220 struct comx_channel *ch = dev->priv;
1221 struct comx_privdata *hw;
1222 struct proc_dir_entry *new_file;
1224 if ((ch->HW_privdata = kmalloc(sizeof(struct comx_privdata),
1225 GFP_KERNEL)) == NULL) {
1228 memset(hw = ch->HW_privdata, 0, sizeof(struct comx_privdata));
1230 if (ch->hardware == &comx_hw || ch->hardware == &cmx_hw) {
1231 hw->memory_size = COMX_MEMORY_SIZE;
1232 hw->io_extent = COMX_IO_EXTENT;
1233 dev->base_addr = COMX_DEFAULT_IO;
1234 dev->irq = COMX_DEFAULT_IRQ;
1235 dev->mem_start = COMX_DEFAULT_MEMADDR;
1236 dev->mem_end = COMX_DEFAULT_MEMADDR + COMX_MEMORY_SIZE;
1237 } else if (ch->hardware == &hicomx_hw) {
1238 hw->memory_size = HICOMX_MEMORY_SIZE;
1239 hw->io_extent = HICOMX_IO_EXTENT;
1240 dev->base_addr = HICOMX_DEFAULT_IO;
1241 dev->irq = HICOMX_DEFAULT_IRQ;
1242 dev->mem_start = HICOMX_DEFAULT_MEMADDR;
1243 dev->mem_end = HICOMX_DEFAULT_MEMADDR + HICOMX_MEMORY_SIZE;
1245 printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
1248 if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, ch->procdir))
1250 goto cleanup_HW_privdata;
1252 new_file->data = (void *)new_file;
1253 new_file->read_proc = &comxhw_read_proc;
1254 new_file->write_proc = &comxhw_write_proc;
1256 new_file->nlink = 1;
1258 if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, ch->procdir))
1260 goto cleanup_filename_io;
1262 new_file->data = (void *)new_file;
1263 new_file->read_proc = &comxhw_read_proc;
1264 new_file->write_proc = &comxhw_write_proc;
1266 new_file->nlink = 1;
1268 if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644,
1269 ch->procdir)) == NULL) {
1270 goto cleanup_filename_irq;
1272 new_file->data = (void *)new_file;
1273 new_file->read_proc = &comxhw_read_proc;
1274 new_file->write_proc = &comxhw_write_proc;
1275 new_file->size = 2; // Ezt tudjuk
1276 new_file->nlink = 1;
1278 if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) {
1279 if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644,
1280 ch->procdir)) == NULL) {
1281 goto cleanup_filename_channel;
1283 new_file->data = (void *)new_file;
1284 new_file->read_proc = &comxhw_read_proc;
1285 new_file->write_proc = &comxhw_write_proc;
1287 new_file->nlink = 1;
1290 if ((new_file = create_proc_entry(FILENAME_MEMADDR, S_IFREG | 0644,
1291 ch->procdir)) == NULL) {
1292 goto cleanup_filename_clock;
1294 new_file->data = (void *)new_file;
1295 new_file->read_proc = &comxhw_read_proc;
1296 new_file->write_proc = &comxhw_write_proc;
1298 new_file->nlink = 1;
1300 if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444,
1301 ch->procdir)) == NULL) {
1302 goto cleanup_filename_memaddr;
1304 new_file->data = (void *)new_file;
1305 new_file->read_proc = &comxhw_read_proc;
1306 new_file->write_proc = NULL;
1307 new_file->nlink = 1;
1309 if ((new_file = create_proc_entry(FILENAME_FIRMWARE, S_IFREG | 0644,
1310 ch->procdir)) == NULL) {
1311 goto cleanup_filename_twin;
1313 new_file->data = (void *)new_file;
1314 new_file->read_proc = &comxhw_read_proc;
1315 new_file->write_proc = &comxhw_write_proc;
1316 new_file->nlink = 1;
1318 if (ch->hardware == &comx_hw) {
1319 ch->HW_board_on = COMX_board_on;
1320 ch->HW_board_off = COMX_board_off;
1321 ch->HW_load_board = COMX_load_board;
1322 } else if (ch->hardware == &cmx_hw) {
1323 ch->HW_board_on = COMX_board_on;
1324 ch->HW_board_off = COMX_board_off;
1325 ch->HW_load_board = CMX_load_board;
1326 ch->HW_set_clock = COMX_set_clock;
1327 } else if (ch->hardware == &hicomx_hw) {
1328 ch->HW_board_on = HICOMX_board_on;
1329 ch->HW_board_off = HICOMX_board_off;
1330 ch->HW_load_board = HICOMX_load_board;
1331 ch->HW_set_clock = COMX_set_clock;
1333 printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
1336 ch->HW_access_board = COMX_access_board;
1337 ch->HW_release_board = COMX_release_board;
1338 ch->HW_txe = COMX_txe;
1339 ch->HW_open = COMX_open;
1340 ch->HW_close = COMX_close;
1341 ch->HW_send_packet = COMX_send_packet;
1342 ch->HW_statistics = COMX_statistics;
1344 if ((ch->twin = comx_twin_check(dev)) != NULL) {
1345 struct comx_channel *twin_ch = ch->twin->priv;
1347 twin_ch->twin = dev;
1353 cleanup_filename_twin:
1354 remove_proc_entry(FILENAME_TWIN, ch->procdir);
1355 cleanup_filename_memaddr:
1356 remove_proc_entry(FILENAME_MEMADDR, ch->procdir);
1357 cleanup_filename_clock:
1358 if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw)
1359 remove_proc_entry(FILENAME_CLOCK, ch->procdir);
1360 cleanup_filename_channel:
1361 remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
1362 cleanup_filename_irq:
1363 remove_proc_entry(FILENAME_IRQ, ch->procdir);
1364 cleanup_filename_io:
1365 remove_proc_entry(FILENAME_IO, ch->procdir);
1366 cleanup_HW_privdata:
1367 kfree(ch->HW_privdata);
1371 /* Called on echo valami >boardtype */
1372 static int COMX_exit(struct net_device *dev)
1374 struct comx_channel *ch = dev->priv;
1375 struct comx_privdata *hw = ch->HW_privdata;
1378 if (hw->firmware->data) kfree(hw->firmware->data);
1379 kfree(hw->firmware);
1381 struct comx_channel *twin_ch = ch->twin->priv;
1383 twin_ch->twin = NULL;
1386 kfree(ch->HW_privdata);
1387 remove_proc_entry(FILENAME_IO, ch->procdir);
1388 remove_proc_entry(FILENAME_IRQ, ch->procdir);
1389 remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
1390 remove_proc_entry(FILENAME_MEMADDR, ch->procdir);
1391 remove_proc_entry(FILENAME_FIRMWARE, ch->procdir);
1392 remove_proc_entry(FILENAME_TWIN, ch->procdir);
1393 if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) {
1394 remove_proc_entry(FILENAME_CLOCK, ch->procdir);
1401 static int COMX_dump(struct net_device *dev)
1403 printk(KERN_INFO "%s: COMX_dump called, why ?\n", dev->name);
1407 static struct comx_hardware comx_hw = {
1416 static struct comx_hardware cmx_hw = {
1425 static struct comx_hardware hicomx_hw = {
1434 static int __init comx_hw_comx_init(void)
1436 comx_register_hardware(&comx_hw);
1437 comx_register_hardware(&cmx_hw);
1438 comx_register_hardware(&hicomx_hw);
1442 static void __exit comx_hw_comx_exit(void)
1444 comx_unregister_hardware("comx");
1445 comx_unregister_hardware("cmx");
1446 comx_unregister_hardware("hicomx");
1449 module_init(comx_hw_comx_init);
1450 module_exit(comx_hw_comx_exit);