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