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