This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / 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  * Daniele Bellucci         <bellucda@tiscali.it>   - 0.87
15  *
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.
20  *
21  * Version 0.80 (99/06/11):
22  *              - port back to kernel, add support builtin driver 
23  *              - cleaned up the source code a bit
24  *
25  * Version 0.81 (99/06/22):
26  *              - cleaned up the board load functions, no more long reset
27  *                timeouts
28  *              - lower modem lines on close
29  *              - some interrupt handling fixes
30  *
31  * Version 0.82 (99/08/24):
32  *              - fix multiple board support
33  *
34  * Version 0.83 (99/11/30):
35  *              - interrupt handling and locking fixes during initalization
36  *              - really fix multiple board support
37  * 
38  * Version 0.84 (99/12/02):
39  *              - some workarounds for problematic hardware/firmware
40  *
41  * Version 0.85 (00/01/14):
42  *              - some additional workarounds :/
43  *              - printk cleanups
44  * Version 0.86 (00/08/15):
45  *              - resource release on failure at COMX_init
46  *
47  * Version 0.87 (03/07/09)
48  *              - audit copy_from_user in comxhw_write_proc
49  */
50
51 #define VERSION "0.87"
52
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>
60
61 #include <asm/uaccess.h>
62 #include <asm/io.h>
63
64 #include "comx.h"
65 #include "comxhw.h"
66
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");
70
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))
75
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))
80
81 #define COMX_CMD(dev, cmd)      (COMX_WRITE(dev, OFF_A_L2_CMD, cmd))
82
83 struct comx_firmware {
84         int     len;
85         unsigned char *data;
86 };
87
88 struct comx_privdata {
89         struct comx_firmware *firmware;
90         u16     clock;
91         char    channel;                // channel no.
92         int     memory_size;
93         short   io_extent;
94         u_long  histogram[5];
95 };
96
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;
101
102 static irqreturn_t COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs);
103
104 static void COMX_board_on(struct net_device *dev)
105 {
106         outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | 
107             COMX_ENABLE_BOARD_IT | COMX_ENABLE_BOARD_MEM), dev->base_addr);
108 }
109
110 static void COMX_board_off(struct net_device *dev)
111 {
112         outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | 
113            COMX_ENABLE_BOARD_IT), dev->base_addr);
114 }
115
116 static void HICOMX_board_on(struct net_device *dev)
117 {
118         outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | 
119            HICOMX_ENABLE_BOARD_MEM), dev->base_addr);
120 }
121
122 static void HICOMX_board_off(struct net_device *dev)
123 {
124         outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | 
125            HICOMX_DISABLE_BOARD_MEM), dev->base_addr);
126 }
127
128 static void COMX_set_clock(struct net_device *dev)
129 {
130         struct comx_channel *ch = dev->priv;
131         struct comx_privdata *hw = ch->HW_privdata;
132
133         COMX_WRITE(dev, OFF_A_L1_CLKINI, hw->clock);
134 }
135
136 static struct net_device *COMX_access_board(struct net_device *dev)
137 {
138         struct comx_channel *ch = dev->priv;
139         struct net_device *ret;
140         int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
141         unsigned long flags;
142
143
144         save_flags(flags); cli();
145         
146         ret = memory_used[mempos];
147
148         if(ret == dev) {
149                 goto out;
150         }
151
152         memory_used[mempos] = dev;
153
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);
157         }
158 out:
159         restore_flags(flags);
160         return ret;
161 }
162
163 static void COMX_release_board(struct net_device *dev, struct net_device *savep)
164 {
165         unsigned long flags;
166         int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
167         struct comx_channel *ch = dev->priv;
168
169         save_flags(flags); cli();
170
171         if (memory_used[mempos] == savep) {
172                 goto out;
173         }
174
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);
179         }
180 out:
181         restore_flags(flags);
182 }
183
184 static int COMX_txe(struct net_device *dev) 
185 {
186         struct net_device *savep;
187         struct comx_channel *ch = dev->priv;
188         int rc = 0;
189
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);
193         } 
194         ch->HW_release_board(dev,savep);
195         if(rc==0xffff) {
196                 printk(KERN_ERR "%s, OFF_A_L2_TxEMPTY is %d\n",dev->name, rc);
197         }
198         return rc;
199 }
200
201 static int COMX_send_packet(struct net_device *dev, struct sk_buff *skb)
202 {
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;
207         word tmp;
208
209         savep = ch->HW_access_board(dev);       
210
211         if (ch->debug_flags & DEBUG_HW_TX) {
212                 comx_debug_bytes(dev, skb->data, skb->len,"COMX_send packet");
213         }
214
215         if (skb->len > COMX_MAX_TX_SIZE) {
216                 ret=FRAME_DROPPED;
217                 goto out;
218         }
219
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;
225
226                 if(dest==0xffff) {
227                         printk(KERN_ERR "%s: OFF_A_L2_TxBUFP is %d\n", dev->name, dest);
228                         ret=FRAME_DROPPED;
229                         goto out;
230                 }
231                                         
232                 writew((unsigned short)skb->len, dev->mem_start + dest);
233                 dest += 2;
234                 while (skb->len > 1) {
235                         writew(*data++, dev->mem_start + dest);
236                         dest += 2; skb->len -= 2;
237                 }
238                 if (skb->len == 1) {
239                         writew(*((byte *)data), dev->mem_start + dest);
240                 }
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;
246         } else {
247                 ch->stats.tx_dropped++;
248                 printk(KERN_INFO "%s: frame dropped\n",dev->name);
249                 if(tmp) {
250                         printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n",dev->name,tmp);
251                 }
252         }
253         
254 out:
255         ch->HW_release_board(dev, savep);
256         dev_kfree_skb(skb);
257         return ret;
258 }
259
260 static inline int comx_read_buffer(struct net_device *dev) 
261 {
262         struct comx_channel *ch = dev->priv;
263         word rbuf_offs;
264         struct sk_buff *skb;
265         word len;
266         int i=0;
267         word *writeptr;
268
269         i = 0;
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);
273                 return 0;
274         }
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);
278                 return 0;
279         }
280         if ((skb = dev_alloc_skb(len + 16)) == NULL) {
281                 ch->stats.rx_dropped++;
282                 COMX_WRITE(dev, OFF_A_L2_DAV, 0);
283                 return 0;
284         }
285         rbuf_offs += 2;
286         skb_reserve(skb, 16);
287         skb_put(skb, len);
288         skb->dev = dev;
289         writeptr = (word *)skb->data;
290         while (i < len) {
291                 *writeptr++ = readw(dev->mem_start + rbuf_offs);
292                 rbuf_offs += 2; 
293                 i += 2;
294         }
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");
300         }
301         ch->LINE_rx(dev, skb);
302         return 1;
303 }
304
305 static inline char comx_line_change(struct net_device *dev, char linestat)
306 {
307         struct comx_channel *ch=dev->priv;
308         char idle=1;
309         
310         
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 +
317                                         HZ*ch->lineup_delay;
318                                 add_timer(&ch->lineup_timer);
319                                 idle=0;
320                         }
321                 } else {
322                         idle=0;
323                         ch->LINE_status(dev, ch->line_status |= LINE_UP);
324                 }
325         } else { /* Vonal le */
326                 idle=0;
327                 if (test_and_clear_bit(0, &ch->lineup_pending)) {
328                         del_timer(&ch->lineup_timer);
329                 } else {
330                         ch->line_status &= ~LINE_UP;
331                         if (ch->LINE_status) {
332                                 ch->LINE_status(dev, ch->line_status);
333                         }
334                 }
335         }
336         return idle;
337 }
338
339
340
341 static irqreturn_t COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs)
342 {
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;
347         unsigned long jiffs;
348         char idle = 0;
349         int count = 0;
350         word tmp;
351
352         if (dev == NULL) {
353                 printk(KERN_ERR "COMX_interrupt: irq %d for unknown device\n", irq);
354                 return IRQ_NONE;
355         }
356
357         jiffs = jiffies;
358
359         interrupted = ch->HW_access_board(dev);
360
361         while (!idle && count < 5000) {
362                 char channel = 0;
363                 idle = 1;
364
365                 while (channel < 2) {
366                         char linestat = 0;
367                         char buffers_emptied = 0;
368
369                         if (channel == 1) {
370                                 if (ch->twin) {
371                                         dev = ch->twin;
372                                         ch = dev->priv;
373                                         hw = ch->HW_privdata;
374                                 } else {
375                                         break;
376                                 }
377                         } else {
378                                 COMX_WRITE(dev, OFF_A_L1_REPENA, 
379                                     COMX_readw(dev, OFF_A_L1_REPENA) & 0xFF00);
380                         }
381                         channel++;
382
383                         if ((ch->init_status & (HW_OPEN | LINE_OPEN)) != 
384                            (HW_OPEN | LINE_OPEN)) {
385                                 continue;
386                         }
387         
388                         /* Collect stats */
389                         tmp = COMX_readw(dev, OFF_A_L1_ABOREC);
390                         COMX_WRITE(dev, OFF_A_L1_ABOREC, 0);
391                         if(tmp==0xffff) {
392                                 printk(KERN_ERR "%s: OFF_A_L1_ABOREC is %d\n",dev->name,tmp);
393                                 break;
394                         } else {
395                                 ch->stats.rx_missed_errors += (tmp >> 8) & 0xff;
396                                 ch->stats.rx_over_errors += tmp & 0xff;
397                         }
398                         tmp = COMX_readw(dev, OFF_A_L1_CRCREC);
399                         COMX_WRITE(dev, OFF_A_L1_CRCREC, 0);
400                         if(tmp==0xffff) {
401                                 printk(KERN_ERR "%s: OFF_A_L1_CRCREC is %d\n",dev->name,tmp);
402                                 break;
403                         } else {
404                                 ch->stats.rx_crc_errors += (tmp >> 8) & 0xff;
405                                 ch->stats.rx_missed_errors += tmp & 0xff;
406                         }
407                         
408                         if ((ch->line_status & LINE_UP) && ch->LINE_rx) {
409                                 tmp=COMX_readw(dev, OFF_A_L2_DAV); 
410                                 while (tmp==1) {
411                                         idle=0;
412                                         buffers_emptied+=comx_read_buffer(dev);
413                                         tmp=COMX_readw(dev, OFF_A_L2_DAV); 
414                                 }
415                                 if(tmp) {
416                                         printk(KERN_ERR "%s: OFF_A_L2_DAV is %d\n", dev->name, tmp);
417                                         break;
418                                 }
419                         }
420
421                         tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY);
422                         if (tmp==1 && ch->LINE_tx) {
423                                 ch->LINE_tx(dev);
424                         } 
425                         if(tmp==0xffff) {
426                                 printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n", dev->name, tmp);
427                                 break;
428                         }
429
430                         if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
431                                 linestat &= ~LINE_UP;
432                         } else {
433                                 linestat |= LINE_UP;
434                         }
435
436                         if ((linestat & LINE_UP) != (ch->line_status & LINE_UP)) {
437                                 ch->stats.tx_carrier_errors++;
438                                 idle &= comx_line_change(dev,linestat);
439                         }
440                                 
441                         hw->histogram[(int)buffers_emptied]++;
442                 }
443                 count++;
444         }
445
446         if(count==5000) {
447                 printk(KERN_WARNING "%s: interrupt stuck\n",dev->name);
448         }
449
450         ch->HW_release_board(dev, interrupted);
451         return IRQ_HANDLED;
452 }
453
454 static int COMX_open(struct net_device *dev)
455 {
456         struct comx_channel *ch = dev->priv;
457         struct comx_privdata *hw = ch->HW_privdata;
458         struct proc_dir_entry *procfile = ch->procdir->subdir;
459         unsigned long jiffs;
460         int twin_open=0;
461         int retval;
462         struct net_device *savep;
463
464         if (!dev->base_addr || !dev->irq || !dev->mem_start) {
465                 return -ENODEV;
466         }
467
468         if (ch->twin && (((struct comx_channel *)(ch->twin->priv))->init_status & HW_OPEN)) {
469                 twin_open=1;
470         }
471
472         if (!twin_open) {
473                 if (!request_region(dev->base_addr, hw->io_extent, dev->name)) {
474                         return -EAGAIN;
475                 }
476                 if (request_irq(dev->irq, COMX_interrupt, 0, dev->name, 
477                    (void *)dev)) {
478                         printk(KERN_ERR "comx-hw-comx: unable to obtain irq %d\n", dev->irq);
479                         release_region(dev->base_addr, hw->io_extent);
480                         return -EAGAIN;
481                 }
482                 ch->init_status |= IRQ_ALLOCATED;
483                 if (!ch->HW_load_board || ch->HW_load_board(dev)) {
484                         ch->init_status &= ~IRQ_ALLOCATED;
485                         retval=-ENODEV;
486                         goto error;
487                 }
488         }
489
490         savep = ch->HW_access_board(dev);
491         COMX_WRITE(dev, OFF_A_L2_LINKUP, 0);
492
493         if (ch->HW_set_clock) {
494                 ch->HW_set_clock(dev);
495         }
496
497         COMX_CMD(dev, COMX_CMD_INIT); 
498         jiffs = jiffies;
499         while (COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && time_before(jiffies, jiffs + HZ)) {
500                 schedule_timeout(1);
501         }
502         
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);
506                 retval=-EIO;
507                 goto error;
508         }
509         udelay(1000);
510
511         COMX_CMD(dev, COMX_CMD_OPEN);
512
513         jiffs = jiffies;
514         while (COMX_readw(dev, OFF_A_L2_LINKUP) != 3 && time_before(jiffies, jiffs + HZ)) {
515                 schedule_timeout(1);
516         }
517         
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);
521                 retval=-EIO;
522                 goto error;
523         }
524         
525         ch->init_status |= HW_OPEN;
526         
527         /* Ez eleg ciki, de ilyen a rendszer */
528         if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
529                 ch->line_status &= ~LINE_UP;
530         } else {
531                 ch->line_status |= LINE_UP;
532         }
533         
534         if (ch->LINE_status) {
535                 ch->LINE_status(dev, ch->line_status);
536         }
537
538         ch->HW_release_board(dev, savep);
539
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;
548                 
549                 }
550         }       
551         
552         return 0;       
553
554 error:
555         if(!twin_open) {
556                 release_region(dev->base_addr, hw->io_extent);
557                 free_irq(dev->irq, (void *)dev);
558         }
559         return retval;
560
561 }
562
563 static int COMX_close(struct net_device *dev)
564 {
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;
570
571         savep = ch->HW_access_board(dev);
572
573         COMX_CMD(dev, COMX_CMD_CLOSE);
574         udelay(1000);
575         COMX_CMD(dev, COMX_CMD_EXIT);
576
577         ch->HW_release_board(dev, savep);
578
579         if (ch->init_status & IRQ_ALLOCATED) {
580                 free_irq(dev->irq, (void *)dev);
581                 ch->init_status &= ~IRQ_ALLOCATED;
582         }
583         release_region(dev->base_addr, hw->io_extent);
584
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;
591                 }
592         }
593
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;
602                 }
603         }
604         
605         ch->init_status &= ~HW_OPEN;
606         return 0;
607 }
608
609 static int COMX_statistics(struct net_device *dev, char *page)
610 {
611         struct comx_channel *ch = dev->priv;
612         struct comx_privdata *hw = ch->HW_privdata;
613         struct net_device *savep;
614         int len = 0;
615
616         savep = ch->HW_access_board(dev);
617
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);
632
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]);
636
637         ch->HW_release_board(dev, savep);
638
639         return len;
640 }
641
642 static int COMX_load_board(struct net_device *dev)
643 {
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;
649         unsigned long flags;
650         unsigned char id1, id2;
651         struct net_device *saved;
652         int retval;
653         int loopcount;
654         int len;
655         byte *COMX_address;
656
657         if (!fw || !fw->len) {
658                 struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
659                 struct comx_privdata *twin_hw;
660
661                 if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
662                         return -EAGAIN;
663                 }
664
665                 if (!(fw = twin_hw->firmware) || !fw->len) {
666                         return -EAGAIN;
667                 }
668         }
669
670         id1 = fw->data[OFF_FW_L1_ID]; 
671         id2 = fw->data[OFF_FW_L1_ID + 1];
672
673         if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_COMX) {
674                 printk(KERN_ERR "%s: incorrect firmware, load aborted\n", 
675                         dev->name);
676                 return -EAGAIN;
677         }
678
679         printk(KERN_INFO "%s: Loading COMX Layer 1 firmware %s\n", dev->name, 
680                 (char *)(fw->data + OFF_FW_L1_ID + 2));
681
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));
687         }
688
689         outb_p(board_segment | COMX_BOARD_RESET, dev->base_addr);
690         /* 10 usec should be enough here */
691         udelay(100);
692
693         save_flags(flags); cli();
694         saved=memory_used[mempos];
695         if(saved) {
696                 ((struct comx_channel *)saved->priv)->HW_board_off(saved);
697         }
698         memory_used[mempos]=dev;
699
700         outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
701
702         writeb(0, dev->mem_start + COMX_JAIL_OFFSET);   
703
704         loopcount=0;
705         while(loopcount++ < 10000 && 
706             readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) {
707                 udelay(100);
708         }       
709         
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));
713                 retval=-ENODEV;
714                 goto out;
715         }
716
717         writeb(0x55, dev->mem_start + 0x18ff);
718         
719         loopcount=0;
720         while(loopcount++ < 10000 && readb(dev->mem_start + 0x18ff) != 0) {
721                 udelay(100);
722         }
723
724         if(readb(dev->mem_start + 0x18ff) != 0) {
725                 printk(KERN_ERR "%s: Can't reset board, reset timeout\n",
726                         dev->name);
727                 retval=-ENODEV;
728                 goto out;
729         }               
730
731         len = 0;
732         COMX_address = (byte *)dev->mem_start;
733         while (fw->len > len) {
734                 writeb(fw->data[len++], COMX_address++);
735         }
736
737         len = 0;
738         COMX_address = (byte *)dev->mem_start;
739         while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
740                 len++;
741         }
742
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]);
747                 retval=-EAGAIN;
748                 goto out;
749         }
750
751         writeb(0, dev->mem_start + COMX_JAIL_OFFSET);
752
753         loopcount = 0;
754         while ( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
755                 udelay(100);
756         }
757
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));
761                 retval=-EAGAIN;
762                 goto out;
763         }
764
765
766         ch->init_status |= FW_LOADED;
767         retval=0;
768
769 out: 
770         outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
771         if(saved) {
772                 ((struct comx_channel *)saved->priv)->HW_board_on(saved);
773         }
774         memory_used[mempos]=saved;
775         restore_flags(flags);
776         return retval;
777 }
778
779 static int CMX_load_board(struct net_device *dev)
780 {
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;
786         #if 0
787         unsigned char id1, id2;
788         #endif
789         struct net_device *saved;
790         unsigned long flags;
791         int retval;
792         int loopcount;
793         int len;
794         byte *COMX_address;
795
796         if (!fw || !fw->len) {
797                 struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
798                 struct comx_privdata *twin_hw;
799
800                 if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
801                         return -EAGAIN;
802                 }
803
804                 if (!(fw = twin_hw->firmware) || !fw->len) {
805                         return -EAGAIN;
806                 }
807         }
808
809         /* Ide kell olyat tenni, hogy ellenorizze az ID-t */
810
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));
814                 return -ENODEV;
815         }
816
817         printk(KERN_INFO "%s: Loading CMX Layer 1 firmware %s\n", dev->name, 
818                 (char *)(fw->data + OFF_FW_L1_ID + 2));
819
820         save_flags(flags); cli();
821         saved=memory_used[mempos];
822         if(saved) {
823                 ((struct comx_channel *)saved->priv)->HW_board_off(saved);
824         }
825         memory_used[mempos]=dev;
826         
827         outb_p(board_segment | COMX_ENABLE_BOARD_MEM | COMX_BOARD_RESET, 
828                 dev->base_addr);
829
830         len = 0;
831         COMX_address = (byte *)dev->mem_start;
832         while (fw->len > len) {
833                 writeb(fw->data[len++], COMX_address++);
834         }
835
836         len = 0;
837         COMX_address = (byte *)dev->mem_start;
838         while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
839                 len++;
840         }
841
842         outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
843
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]);
848                 retval=-EAGAIN;
849                 goto out;
850         }
851
852         loopcount=0;
853         while( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
854                 udelay(100);
855         }
856
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));
860                 retval=-EAGAIN;
861                 goto out;
862         }
863
864         ch->init_status |= FW_LOADED;
865         retval=0;
866
867 out: 
868         outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
869         if(saved) {
870                 ((struct comx_channel *)saved->priv)->HW_board_on(saved);
871         }
872         memory_used[mempos]=saved;
873         restore_flags(flags);
874         return retval;
875 }
876
877 static int HICOMX_load_board(struct net_device *dev)
878 {
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;
886         unsigned long flags;
887         int retval;
888         int loopcount;
889         int len;
890         word *HICOMX_address;
891         char id = 1;
892
893         if (!fw || !fw->len) {
894                 struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
895                 struct comx_privdata *twin_hw;
896
897                 if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
898                         return -EAGAIN;
899                 }
900
901                 if (!(fw = twin_hw->firmware) || !fw->len) {
902                         return -EAGAIN;
903                 }
904         }
905
906         while (id != 4) {
907                 if (inb_p(dev->base_addr + id++) != HICOMX_ID_BYTE) {
908                         break;
909                 }
910         }
911
912         if (id != 4) {
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));
916                 return -1;      
917         }
918
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);
923                 return -EAGAIN;
924         }
925
926         printk(KERN_INFO "%s: Loading HICOMX Layer 1 firmware %s\n", dev->name, 
927                 (char *)(fw->data + OFF_FW_L1_ID + 2));
928
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));
934         }
935
936         outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
937         udelay(10);     
938
939         save_flags(flags); cli();
940         saved=memory_used[mempos];
941         if(saved) {
942                 ((struct comx_channel *)saved->priv)->HW_board_off(saved);
943         }
944         memory_used[mempos]=dev;
945
946         outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
947         outb_p(HICOMX_PRG_MEM, dev->base_addr + 1);
948
949         len = 0;
950         HICOMX_address = (word *)dev->mem_start;
951         while (fw->len > len) {
952                 writeb(fw->data[len++], HICOMX_address++);
953         }
954
955         len = 0;
956         HICOMX_address = (word *)dev->mem_start;
957         while (len != fw->len && (readw(HICOMX_address++) & 0xff) == fw->data[len]) {
958                 len++;
959         }
960
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]);
965                 retval=-EAGAIN;
966                 goto out;
967         }
968
969         outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
970         outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
971
972         outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
973
974         loopcount=0;
975         while(loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
976                 udelay(100);
977         }
978
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));
982                 retval=-EAGAIN;
983                 goto out;
984         }
985
986         ch->init_status |= FW_LOADED;
987         retval=0;
988
989 out:
990         outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr);
991         outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
992
993         if(saved) {
994                 ((struct comx_channel *)saved->priv)->HW_board_on(saved);
995         }
996         memory_used[mempos]=saved;
997         restore_flags(flags);
998         return retval;
999 }
1000
1001 static struct net_device *comx_twin_check(struct net_device *dev)
1002 {
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;
1006
1007         struct net_device *twin;
1008         struct comx_channel *ch_twin;
1009         struct comx_privdata *hw_twin;
1010
1011
1012         for ( ; procfile ; procfile = procfile->next) {
1013         
1014                 if(!S_ISDIR(procfile->mode)) {
1015                         continue;
1016                 }
1017         
1018                 twin=procfile->data;
1019                 ch_twin=twin->priv;
1020                 hw_twin=ch_twin->HW_privdata;
1021
1022
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) {
1028                         return twin;
1029                 }
1030         }
1031         return NULL;
1032 }
1033
1034 static int comxhw_write_proc(struct file *file, const char *buffer, 
1035         u_long count, void *data)
1036 {
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;
1041         char *page;
1042
1043
1044         if(ch->init_status & HW_OPEN) {
1045                 return -EAGAIN; 
1046         }
1047         
1048         if (strcmp(FILENAME_FIRMWARE, entry->name) != 0) {
1049                 if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
1050                         return -ENOMEM;
1051                 }
1052                 if(copy_from_user(page, buffer, count = (min_t(int, count, PAGE_SIZE))))
1053                 {
1054                         count = -EFAULT;
1055                         goto out;
1056                 }
1057                 if (page[count-1] == '\n')
1058                         page[count-1] = '\0';
1059                 else if (count < PAGE_SIZE)
1060                         page[count] = '\0';
1061                 else if (page[count]) {
1062                         count = -EINVAL;
1063                         goto out;
1064                 }
1065                 page[count]=0;  /* Null terminate */
1066         } else {
1067                 byte *tmp;
1068
1069                 if (!hw->firmware) {
1070                         if ((hw->firmware = kmalloc(sizeof(struct comx_firmware), 
1071                             GFP_KERNEL)) == NULL) {
1072                                 return -ENOMEM;
1073                         }
1074                         hw->firmware->len = 0;
1075                         hw->firmware->data = NULL;
1076                 }
1077                 
1078                 if ((tmp = kmalloc(count + file->f_pos, GFP_KERNEL)) == NULL) {
1079                         return -ENOMEM;
1080                 }
1081                 
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);
1086                 }
1087                 if (hw->firmware->data) {
1088                         kfree(hw->firmware->data);
1089                 }
1090                 if (copy_from_user(tmp + file->f_pos, buffer, count))
1091                         return -EFAULT;
1092                 hw->firmware->len = entry->size = file->f_pos + count;
1093                 hw->firmware->data = tmp;
1094                 file->f_pos += count;
1095                 return count;
1096         }
1097
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");
1102                         hw->channel = 0;
1103                 }
1104                 if ((ch->twin = comx_twin_check(dev)) != NULL) {
1105                         struct comx_channel *twin_ch = ch->twin->priv;
1106                         twin_ch->twin = dev;
1107                 }
1108         } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
1109                 dev->irq = simple_strtoul(page, NULL, 0);
1110                 if (dev->irq == 2) {
1111                         dev->irq = 9;
1112                 }
1113                 if (dev->irq < 3 || dev->irq > 15) {
1114                         printk(KERN_ERR "comxhw: Invalid irq number\n");
1115                         dev->irq = 0;
1116                 }
1117                 if ((ch->twin = comx_twin_check(dev)) != NULL) {
1118                         struct comx_channel *twin_ch = ch->twin->priv;
1119                         twin_ch->twin = dev;
1120                 }
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");
1126                         dev->base_addr = 0;
1127                 }
1128                 if ((ch->twin = comx_twin_check(dev)) != NULL) {
1129                         struct comx_channel *twin_ch = ch->twin->priv;
1130
1131                         twin_ch->twin = dev;
1132                 }
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;
1137                 }
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");
1141                         dev->mem_start = 0;
1142                 }
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;
1146
1147                         twin_ch->twin = dev;
1148                 }
1149         } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
1150                 if (strncmp("ext", page, 3) == 0) {
1151                         hw->clock = 0;
1152                 } else {
1153                         int kbps;
1154
1155                         kbps = simple_strtoul(page, NULL, 0);
1156                         hw->clock = kbps ? COMX_CLOCK_CONST/kbps : 0;
1157                 }
1158         }
1159 out:
1160         free_page((unsigned long)page);
1161         return count;
1162 }
1163
1164 static int comxhw_read_proc(char *page, char **start, off_t off, int count,
1165         int *eof, void *data)
1166 {
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;
1171         int len = 0;
1172
1173
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) {
1185                 if (hw->clock) {
1186                         len = sprintf(page, "%-8d\n", COMX_CLOCK_CONST/hw->clock);
1187                 } else {
1188                         len = sprintf(page, "external\n");
1189                 }
1190         } else if (strcmp(file->name, FILENAME_FIRMWARE) == 0) {
1191                 len = min_t(int, FILE_PAGESIZE,
1192                           min_t(int, count, 
1193                               hw->firmware ?
1194                               (hw->firmware->len - off) : 0));
1195                 if (len < 0) {
1196                         len = 0;
1197                 }
1198                 *start = hw->firmware ? (hw->firmware->data + off) : NULL;
1199                 if (off + len >= (hw->firmware ? hw->firmware->len : 0) || len == 0) {
1200                         *eof = 1;
1201                 }
1202                 return len;
1203         }       
1204
1205         if (off >= len) {
1206                 *eof = 1;
1207                 return 0;
1208         }
1209
1210         *start = page + off;
1211         if (count >= len - off) {
1212                 *eof = 1;
1213         }
1214         return min_t(int, count, len - off);
1215 }
1216
1217 /* Called on echo comx >boardtype */
1218 static int COMX_init(struct net_device *dev)
1219 {
1220         struct comx_channel *ch = dev->priv;
1221         struct comx_privdata *hw;
1222         struct proc_dir_entry *new_file;
1223
1224         if ((ch->HW_privdata = kmalloc(sizeof(struct comx_privdata), 
1225             GFP_KERNEL)) == NULL) {
1226                 return -ENOMEM;
1227         }
1228         memset(hw = ch->HW_privdata, 0, sizeof(struct comx_privdata));
1229
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;
1244         } else {
1245                 printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
1246         }
1247
1248         if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, ch->procdir))
1249             == NULL) {
1250             goto cleanup_HW_privdata;
1251         }
1252         new_file->data = (void *)new_file;
1253         new_file->read_proc = &comxhw_read_proc;
1254         new_file->write_proc = &comxhw_write_proc;
1255         new_file->size = 6;
1256         new_file->nlink = 1;
1257
1258         if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, ch->procdir))
1259             == NULL) {
1260             goto cleanup_filename_io;
1261         }
1262         new_file->data = (void *)new_file;
1263         new_file->read_proc = &comxhw_read_proc;
1264         new_file->write_proc = &comxhw_write_proc;
1265         new_file->size = 5;
1266         new_file->nlink = 1;
1267
1268         if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644, 
1269             ch->procdir)) == NULL) {
1270             goto cleanup_filename_irq;
1271         }
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;
1277
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;
1282                 }
1283                 new_file->data = (void *)new_file;
1284                 new_file->read_proc = &comxhw_read_proc;
1285                 new_file->write_proc = &comxhw_write_proc;
1286                 new_file->size = 9;
1287                 new_file->nlink = 1;
1288         }
1289
1290         if ((new_file = create_proc_entry(FILENAME_MEMADDR, S_IFREG | 0644, 
1291             ch->procdir)) == NULL) {
1292                     goto cleanup_filename_clock;
1293         }
1294         new_file->data = (void *)new_file;
1295         new_file->read_proc = &comxhw_read_proc;
1296         new_file->write_proc = &comxhw_write_proc;
1297         new_file->size = 8;
1298         new_file->nlink = 1;
1299
1300         if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, 
1301             ch->procdir)) == NULL) {
1302                     goto cleanup_filename_memaddr;
1303         }
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;
1308
1309         if ((new_file = create_proc_entry(FILENAME_FIRMWARE, S_IFREG | 0644, 
1310             ch->procdir)) == NULL) {
1311                     goto cleanup_filename_twin;
1312         }
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;
1317
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;
1332         } else {
1333                 printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
1334         }
1335
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;
1343
1344         if ((ch->twin = comx_twin_check(dev)) != NULL) {
1345                 struct comx_channel *twin_ch = ch->twin->priv;
1346
1347                 twin_ch->twin = dev;
1348         }
1349
1350         MOD_INC_USE_COUNT;
1351         return 0;
1352
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);
1368         return -EIO;
1369 }
1370
1371 /* Called on echo valami >boardtype */
1372 static int COMX_exit(struct net_device *dev)
1373 {
1374         struct comx_channel *ch = dev->priv;
1375         struct comx_privdata *hw = ch->HW_privdata;
1376
1377         if (hw->firmware) {
1378                 if (hw->firmware->data) kfree(hw->firmware->data);
1379                 kfree(hw->firmware);
1380         } if (ch->twin) {
1381                 struct comx_channel *twin_ch = ch->twin->priv;
1382
1383                 twin_ch->twin = NULL;
1384         }
1385         
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);
1395         }
1396
1397         MOD_DEC_USE_COUNT;
1398         return 0;
1399 }
1400
1401 static int COMX_dump(struct net_device *dev)
1402 {
1403         printk(KERN_INFO "%s: COMX_dump called, why ?\n", dev->name);
1404         return 0;
1405 }
1406
1407 static struct comx_hardware comx_hw = {
1408         "comx",
1409         VERSION,
1410         COMX_init,
1411         COMX_exit,
1412         COMX_dump,
1413         NULL
1414 };
1415
1416 static struct comx_hardware cmx_hw = {
1417         "cmx",
1418         VERSION,
1419         COMX_init,
1420         COMX_exit,
1421         COMX_dump,
1422         NULL
1423 };
1424
1425 static struct comx_hardware hicomx_hw = {
1426         "hicomx",
1427         VERSION,
1428         COMX_init,
1429         COMX_exit,
1430         COMX_dump,
1431         NULL
1432 };
1433
1434 static int __init comx_hw_comx_init(void)
1435 {
1436         comx_register_hardware(&comx_hw);
1437         comx_register_hardware(&cmx_hw);
1438         comx_register_hardware(&hicomx_hw);
1439         return 0;
1440 }
1441
1442 static void __exit comx_hw_comx_exit(void)
1443 {
1444         comx_unregister_hardware("comx");
1445         comx_unregister_hardware("cmx");
1446         comx_unregister_hardware("hicomx");
1447 }
1448
1449 module_init(comx_hw_comx_init);
1450 module_exit(comx_hw_comx_exit);