File: /usr/src/linux/drivers/net/wan/comx-hw-comx.c

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     
64     MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>, Tivadar Szemethy <tiv@itc.hu>, Arpad Bakay");
65     MODULE_DESCRIPTION("Hardware-level driver for the COMX and HICOMX adapters\n");
66     MODULE_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     
80     struct comx_firmware {
81     	int	len;
82     	unsigned char *data;
83     };
84     
85     struct 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     
94     static struct net_device *memory_used[(COMX_MEM_MAX - COMX_MEM_MIN) / 0x10000];
95     extern struct comx_hardware hicomx_hw;
96     extern struct comx_hardware comx_hw;
97     extern struct comx_hardware cmx_hw;
98     
99     static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs);
100     
101     static 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     
107     static 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     
113     static 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     
119     static 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     
125     static 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     
133     static 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     	}
155     out:
156     	restore_flags(flags);
157     	return ret;
158     }
159     
160     static 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     	}
177     out:
178     	restore_flags(flags);
179     }
180     
181     static 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     
198     static 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     	
251     out:
252     	ch->HW_release_board(dev, savep);
253     	dev_kfree_skb(skb);
254     	return ret;
255     }
256     
257     static 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     
302     static 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     
338     static 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     
450     static 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 (check_region(dev->base_addr, hw->io_extent)) {
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     			return -EAGAIN;
476     		}
477     		ch->init_status |= IRQ_ALLOCATED;
478     		request_region(dev->base_addr, hw->io_extent, dev->name);
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     
550     error:
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     
559     static 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     
605     static 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     
638     static 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     
765     out: 
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     
775     static 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     
863     out: 
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     
873     static 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     
985     out:
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     
997     static 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     
1030     static 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     	}
1154     out:
1155     	free_page((unsigned long)page);
1156     	return count;
1157     }
1158     
1159     static 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 */
1213     static 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     
1348     cleanup_filename_twin:
1349     	remove_proc_entry(FILENAME_TWIN, ch->procdir);
1350     cleanup_filename_memaddr:
1351     	remove_proc_entry(FILENAME_MEMADDR, ch->procdir);
1352     cleanup_filename_clock:
1353     	if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw)
1354     		remove_proc_entry(FILENAME_CLOCK, ch->procdir);
1355     cleanup_filename_channel:
1356     	remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
1357     cleanup_filename_irq:
1358     	remove_proc_entry(FILENAME_IRQ, ch->procdir);
1359     cleanup_filename_io:
1360     	remove_proc_entry(FILENAME_IO, ch->procdir);
1361     cleanup_HW_privdata:
1362     	kfree(ch->HW_privdata);
1363     	return -EIO;
1364     }
1365     
1366     /* Called on echo valami >boardtype */
1367     static 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     
1396     static 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     
1402     static struct comx_hardware comx_hw = {
1403     	"comx",
1404     	VERSION,
1405     	COMX_init,
1406     	COMX_exit,
1407     	COMX_dump,
1408     	NULL
1409     };
1410     
1411     static struct comx_hardware cmx_hw = {
1412     	"cmx",
1413     	VERSION,
1414     	COMX_init,
1415     	COMX_exit,
1416     	COMX_dump,
1417     	NULL
1418     };
1419     
1420     static 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     
1433     int __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
1443     void cleanup_module(void)
1444     {
1445     	comx_unregister_hardware("comx");
1446     	comx_unregister_hardware("cmx");
1447     	comx_unregister_hardware("hicomx");
1448     }
1449     #endif
1450