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

1     /* 
2      * Hardware driver for the MixCom synchronous serial board 
3      *
4      * Author: Gergely Madarasz <gorgo@itc.hu>
5      *
6      * based on skeleton driver code and a preliminary hscx driver by 
7      * Tivadar Szemethy <tiv@itc.hu>
8      *
9      * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu>
10      *
11      * Contributors:
12      * Arnaldo Carvalho de Melo <acme@conectiva.com.br> (0.65)
13      *
14      * This program is free software; you can redistribute it and/or
15      * modify it under the terms of the GNU General Public License
16      * as published by the Free Software Foundation; either version
17      * 2 of the License, or (at your option) any later version.
18      *
19      * Version 0.60 (99/06/11):
20      *		- ported to the kernel, now works as builtin code
21      *
22      * Version 0.61 (99/06/11):
23      *		- recognize the one-channel MixCOM card (id byte = 0x13)
24      *		- printk fixes
25      * 
26      * Version 0.62 (99/07/15):
27      *		- fixes according to the new hw docs 
28      *		- report line status when open
29      *
30      * Version 0.63 (99/09/21):
31      *		- line status report fixes
32      *
33      * Version 0.64 (99/12/01):
34      *		- some more cosmetical fixes
35      *
36      * Version 0.65 (00/08/15)
37      *		- resource release on failure at MIXCOM_init
38      */
39     
40     #define VERSION "0.65"
41     
42     #include <linux/module.h>
43     #include <linux/version.h>
44     #include <linux/types.h>
45     #include <linux/sched.h>
46     #include <linux/netdevice.h>
47     #include <linux/proc_fs.h>
48     #include <asm/types.h>
49     #include <asm/uaccess.h>
50     #include <asm/io.h>
51     #include <linux/ioport.h>
52     #include <linux/delay.h>
53     #include <linux/init.h>
54     
55     #include "comx.h"
56     #include "mixcom.h"
57     #include "hscx.h"
58     
59     MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
60     MODULE_DESCRIPTION("Hardware-level driver for the serial port of the MixCom board");
61     MODULE_LICENSE("GPL");
62     
63     #define MIXCOM_DATA(d) ((struct mixcom_privdata *)(COMX_CHANNEL(d)-> \
64     	HW_privdata))
65     
66     #define MIXCOM_BOARD_BASE(d) (d->base_addr - MIXCOM_SERIAL_OFFSET - \
67     	(1 - MIXCOM_DATA(d)->channel) * MIXCOM_CHANNEL_OFFSET)
68     
69     #define MIXCOM_DEV_BASE(port,channel) (port + MIXCOM_SERIAL_OFFSET + \
70     	(1 - channel) * MIXCOM_CHANNEL_OFFSET)
71     
72     /* Values used to set the IRQ line */
73     static unsigned char mixcom_set_irq[]={0xFF, 0xFF, 0xFF, 0x0, 0xFF, 0x2, 0x4, 0x6, 0xFF, 0xFF, 0x8, 0xA, 0xC, 0xFF, 0xE, 0xFF};
74     
75     static unsigned char* hscx_versions[]={"A1", NULL, "A2", NULL, "A3", "2.1"};
76     
77     struct mixcom_privdata {
78     	u16	clock;
79     	char	channel;
80     	long	txbusy;
81     	struct sk_buff *sending;
82     	unsigned tx_ptr;
83     	struct sk_buff *recving;
84     	unsigned rx_ptr;
85     	unsigned char status;
86     	char	card_has_status;
87     };
88     
89     static inline void wr_hscx(struct net_device *dev, int reg, unsigned char val) 
90     {
91     	outb(val, dev->base_addr + reg);
92     }
93     
94     static inline unsigned char rd_hscx(struct net_device *dev, int reg)
95     {
96     	return inb(dev->base_addr + reg);
97     }
98     
99     static inline void hscx_cmd(struct net_device *dev, int cmd)
100     {
101     	unsigned long jiffs = jiffies;
102     	unsigned char cec;
103     	unsigned delay = 0;
104     
105     	while ((cec = (rd_hscx(dev, HSCX_STAR) & HSCX_CEC) != 0) && 
106     	    (jiffs + HZ > jiffies)) {
107     		udelay(1);
108     		if (++delay > (100000 / HZ)) break;
109     	}
110     	if (cec) {
111     		printk(KERN_WARNING "%s: CEC stuck, probably no clock!\n",dev->name);
112     	} else {
113     		wr_hscx(dev, HSCX_CMDR, cmd);
114     	}
115     }
116     
117     static inline void hscx_fill_fifo(struct net_device *dev)
118     {
119     	struct comx_channel *ch = dev->priv;
120     	struct mixcom_privdata *hw = ch->HW_privdata;
121     	register word to_send = hw->sending->len - hw->tx_ptr;
122     
123     
124     	outsb(dev->base_addr + HSCX_FIFO,
125             	&(hw->sending->data[hw->tx_ptr]), min_t(unsigned int, to_send, 32));
126     	if (to_send <= 32) {
127             	hscx_cmd(dev, HSCX_XTF | HSCX_XME);
128     	        kfree_skb(hw->sending);
129             	hw->sending = NULL; 
130             	hw->tx_ptr = 0;
131             } else {
132     	        hscx_cmd(dev, HSCX_XTF);
133             	hw->tx_ptr += 32;
134             }
135     }
136     
137     static inline void hscx_empty_fifo(struct net_device *dev, int cnt)
138     {
139     	struct comx_channel *ch = dev->priv;
140     	struct mixcom_privdata *hw = ch->HW_privdata;
141     
142     	if (hw->recving == NULL) {
143             	if (!(hw->recving = dev_alloc_skb(HSCX_MTU + 16))) {
144     	                ch->stats.rx_dropped++;
145             	        hscx_cmd(dev, HSCX_RHR);
146                     } else {
147     	                skb_reserve(hw->recving, 16);
148             	        skb_put(hw->recving, HSCX_MTU);
149                     }
150     	        hw->rx_ptr = 0;
151             }
152     	if (cnt > 32 || !cnt || hw->recving == NULL) {
153             	printk(KERN_ERR "hscx_empty_fifo: cnt is %d, hw->recving %p\n",
154     		        cnt, (void *)hw->recving);
155     	        return;
156             }
157             
158     	insb(dev->base_addr + HSCX_FIFO, &(hw->recving->data[hw->rx_ptr]),cnt);
159     	hw->rx_ptr += cnt;
160     	hscx_cmd(dev, HSCX_RMC);
161     }
162     
163     
164     static int MIXCOM_txe(struct net_device *dev)
165     {
166     	struct comx_channel *ch = dev->priv;
167     	struct mixcom_privdata *hw = ch->HW_privdata;
168     
169     	return !test_bit(0, &hw->txbusy);
170     }
171     
172     static int mixcom_probe(struct net_device *dev)
173     {
174     	unsigned long flags;
175     	int id, vstr, ret=0;
176     
177     	save_flags(flags); cli();
178     
179     	id=inb_p(MIXCOM_BOARD_BASE(dev) + MIXCOM_ID_OFFSET) & 0x7f;
180     
181      	if (id != MIXCOM_ID ) {
182     		ret=-ENODEV;
183     		printk(KERN_WARNING "%s: no MixCOM board found at 0x%04lx\n",dev->name, dev->base_addr);
184     		goto out;
185     	}
186     
187     	vstr=inb_p(dev->base_addr + HSCX_VSTR) & 0x0f;
188     	if(vstr>=sizeof(hscx_versions)/sizeof(char*) || 
189     	    hscx_versions[vstr]==NULL) {
190     		printk(KERN_WARNING "%s: board found but no HSCX chip detected at 0x%4lx (vstr = 0x%1x)\n",dev->name,dev->base_addr,vstr);
191     		ret = -ENODEV;
192     	} else {
193     		printk(KERN_INFO "%s: HSCX chip version %s\n",dev->name,hscx_versions[vstr]);
194     		ret = 0;
195     	}
196     
197     out:
198     
199     	restore_flags(flags);
200     	return ret;
201     }
202     
203     #if 0
204     static void MIXCOM_set_clock(struct net_device *dev)
205     {
206     	struct comx_channel *ch = dev->priv;
207     	struct mixcom_privdata *hw = ch->HW_privdata;
208     
209     	if (hw->clock) {
210     		;
211     	} else {
212     		;
213     	}
214     }
215     #endif
216     
217     static void mixcom_board_on(struct net_device *dev)
218     {
219     	outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
220     	udelay(1000);
221     	outb_p(mixcom_set_irq[dev->irq] | MIXCOM_ON, 
222     		MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
223     	udelay(1000);
224     }
225     
226     static void mixcom_board_off(struct net_device *dev)
227     {
228     	outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
229     	udelay(1000);
230     }
231     
232     static void mixcom_off(struct net_device *dev)
233     {
234     	wr_hscx(dev, HSCX_CCR1, 0x0);
235     }
236     
237     static void mixcom_on(struct net_device *dev)
238     {
239     	struct comx_channel *ch = dev->priv;
240     
241     	wr_hscx(dev, HSCX_CCR1, HSCX_PU | HSCX_ODS | HSCX_ITF); // power up, push-pull
242     	wr_hscx(dev, HSCX_CCR2, HSCX_CIE /* | HSCX_RIE */ );
243     	wr_hscx(dev, HSCX_MODE, HSCX_TRANS | HSCX_ADM8 | HSCX_RAC | HSCX_RTS );
244     	wr_hscx(dev, HSCX_RLCR, HSCX_RC | 47); // 1504 bytes
245     	wr_hscx(dev, HSCX_MASK, HSCX_RSC | HSCX_TIN );
246     	hscx_cmd(dev, HSCX_XRES | HSCX_RHR);
247     
248     	if (ch->HW_set_clock) ch->HW_set_clock(dev);
249     
250     }
251     
252     static int MIXCOM_send_packet(struct net_device *dev, struct sk_buff *skb) 
253     {
254     	struct comx_channel *ch = dev->priv;
255     	struct mixcom_privdata *hw = ch->HW_privdata;
256     	unsigned long flags;
257     
258     	if (ch->debug_flags & DEBUG_HW_TX) {
259     		comx_debug_bytes(dev, skb->data, skb->len, "MIXCOM_send_packet");
260     	}
261     
262     	if (!(ch->line_status & LINE_UP)) {
263     		return FRAME_DROPPED;
264     	}
265     
266     	if (skb->len > HSCX_MTU) {
267     		ch->stats.tx_errors++;	
268     		return FRAME_ERROR;
269     	}
270     
271     	save_flags(flags); cli();
272     
273     	if (test_and_set_bit(0, &hw->txbusy)) {
274     		printk(KERN_ERR "%s: transmitter called while busy... dropping frame (length %d)\n", dev->name, skb->len);
275     		restore_flags(flags);
276     		return FRAME_DROPPED;
277     	}
278     
279     
280     	hw->sending = skb;
281     	hw->tx_ptr = 0;
282     	hw->txbusy = 1;
283     //	atomic_inc(&skb->users);	// save it
284     	hscx_fill_fifo(dev);
285     	restore_flags(flags);
286     
287     	ch->stats.tx_packets++;
288     	ch->stats.tx_bytes += skb->len; 
289     
290     	if (ch->debug_flags & DEBUG_HW_TX) {
291     		comx_debug(dev, "MIXCOM_send_packet was successful\n\n");
292     	}
293     
294     	return FRAME_ACCEPTED;
295     }
296     
297     static inline void mixcom_receive_frame(struct net_device *dev) 
298     {
299     	struct comx_channel *ch=dev->priv;
300     	struct mixcom_privdata *hw=ch->HW_privdata;
301     	register byte rsta;
302     	register word length;
303     
304     	rsta = rd_hscx(dev, HSCX_RSTA) & (HSCX_VFR | HSCX_RDO | 
305     		HSCX_CRC | HSCX_RAB);
306     	length = ((rd_hscx(dev, HSCX_RBCH) & 0x0f) << 8) | 
307     		rd_hscx(dev, HSCX_RBCL);
308     
309     	if ( length > hw->rx_ptr ) {
310     		hscx_empty_fifo(dev, length - hw->rx_ptr);
311     	}
312     	
313     	if (!(rsta & HSCX_VFR)) {
314     		ch->stats.rx_length_errors++;
315     	}
316     	if (rsta & HSCX_RDO) {
317     		ch->stats.rx_over_errors++;
318     	}
319     	if (!(rsta & HSCX_CRC)) {
320     		ch->stats.rx_crc_errors++;
321     	}
322     	if (rsta & HSCX_RAB) {
323     		ch->stats.rx_frame_errors++;
324     	}
325     	ch->stats.rx_packets++; 
326     	ch->stats.rx_bytes += length;
327     
328     	if (rsta == (HSCX_VFR | HSCX_CRC) && hw->recving) {
329     		skb_trim(hw->recving, hw->rx_ptr - 1);
330     		if (ch->debug_flags & DEBUG_HW_RX) {
331     			comx_debug_skb(dev, hw->recving,
332     				"MIXCOM_interrupt receiving");
333     		}
334     		hw->recving->dev = dev;
335     		if (ch->LINE_rx) {
336     			ch->LINE_rx(dev, hw->recving);
337     		}
338     	}
339     	else if(hw->recving) {
340     		kfree_skb(hw->recving);
341     	}
342     	hw->recving = NULL; 
343     	hw->rx_ptr = 0;
344     }
345     
346     
347     static inline void mixcom_extended_interrupt(struct net_device *dev) 
348     {
349     	struct comx_channel *ch=dev->priv;
350     	struct mixcom_privdata *hw=ch->HW_privdata;
351     	register byte exir;
352     
353     	exir = rd_hscx(dev, HSCX_EXIR) & (HSCX_XDU | HSCX_RFO | HSCX_CSC );
354     
355     	if (exir & HSCX_RFO) {
356     		ch->stats.rx_over_errors++;
357     		if (hw->rx_ptr) {
358     			kfree_skb(hw->recving);
359     			hw->recving = NULL; hw->rx_ptr = 0;
360     		}
361     		printk(KERN_ERR "MIXCOM: rx overrun\n");
362     		hscx_cmd(dev, HSCX_RHR);
363     	}
364     
365     	if (exir & HSCX_XDU) { // xmit underrun
366     		ch->stats.tx_errors++;
367     		ch->stats.tx_aborted_errors++;
368     		if (hw->tx_ptr) {
369     			kfree_skb(hw->sending);
370     			hw->sending = NULL; 
371     			hw->tx_ptr = 0;
372     		}
373     		hscx_cmd(dev, HSCX_XRES);
374     		clear_bit(0, &hw->txbusy);
375     		if (ch->LINE_tx) {
376     			ch->LINE_tx(dev);
377     		}
378     		printk(KERN_ERR "MIXCOM: tx underrun\n");
379     	}
380     
381     	if (exir & HSCX_CSC) {        
382     		ch->stats.tx_carrier_errors++;
383     		if ((rd_hscx(dev, HSCX_STAR) & HSCX_CTS) == 0) { // Vonal le
384     			if (test_and_clear_bit(0, &ch->lineup_pending)) {
385                    			del_timer(&ch->lineup_timer);
386     			} else if (ch->line_status & LINE_UP) {
387             		       	ch->line_status &= ~LINE_UP;
388                     		if (ch->LINE_status) {
389                           			ch->LINE_status(dev,ch->line_status);
390                           		}
391     		      	}
392     		}
393     		if (!(ch->line_status & LINE_UP) && (rd_hscx(dev, HSCX_STAR) & 
394     		    HSCX_CTS)) { // Vonal fol
395     			if (!test_and_set_bit(0,&ch->lineup_pending)) {
396     				ch->lineup_timer.function = comx_lineup_func;
397     	        	        ch->lineup_timer.data = (unsigned long)dev;
398             	        	ch->lineup_timer.expires = jiffies + HZ * 
399             	        		ch->lineup_delay;
400     	                	add_timer(&ch->lineup_timer);
401     		                hscx_cmd(dev, HSCX_XRES);
402             		        clear_bit(0, &hw->txbusy);
403                     		if (hw->sending) {
404     					kfree_skb(hw->sending);
405     				}
406     				hw->sending=NULL;
407     				hw->tx_ptr = 0;
408     			}
409     		}
410     	}
411     }
412     
413     
414     static void MIXCOM_interrupt(int irq, void *dev_id, struct pt_regs *regs)
415     {
416     	unsigned long flags;
417     	struct net_device *dev = (struct net_device *)dev_id;
418     	struct comx_channel *ch, *twin_ch;
419     	struct mixcom_privdata *hw, *twin_hw;
420     	register unsigned char ista;
421     
422     	if (dev==NULL) {
423     		printk(KERN_ERR "comx_interrupt: irq %d for unknown device\n",irq);
424     		return;
425     	}
426     
427     	ch = dev->priv; 
428     	hw = ch->HW_privdata;
429     
430     	save_flags(flags); cli(); 
431     
432     	while((ista = (rd_hscx(dev, HSCX_ISTA) & (HSCX_RME | HSCX_RPF | 
433     	    HSCX_XPR | HSCX_EXB | HSCX_EXA | HSCX_ICA)))) {
434     		register byte ista2 = 0;
435     
436     		if (ista & HSCX_RME) {
437     			mixcom_receive_frame(dev);
438     		}
439     		if (ista & HSCX_RPF) {
440     			hscx_empty_fifo(dev, 32);
441     		}
442     		if (ista & HSCX_XPR) {
443     			if (hw->tx_ptr) {
444     				hscx_fill_fifo(dev);
445     			} else {
446     				clear_bit(0, &hw->txbusy);
447                    			ch->LINE_tx(dev);
448     			}
449     		}
450     		
451     		if (ista & HSCX_EXB) {
452     			mixcom_extended_interrupt(dev);
453     		}
454     		
455     		if ((ista & HSCX_EXA) && ch->twin)  {
456     			mixcom_extended_interrupt(ch->twin);
457     		}
458     	
459     		if ((ista & HSCX_ICA) && ch->twin &&
460     		    (ista2 = rd_hscx(ch->twin, HSCX_ISTA) &
461     		    (HSCX_RME | HSCX_RPF | HSCX_XPR ))) {
462     			if (ista2 & HSCX_RME) {
463     				mixcom_receive_frame(ch->twin);
464     			}
465     			if (ista2 & HSCX_RPF) {
466     				hscx_empty_fifo(ch->twin, 32);
467     			}
468     			if (ista2 & HSCX_XPR) {
469     				twin_ch=ch->twin->priv;
470     				twin_hw=twin_ch->HW_privdata;
471     				if (twin_hw->tx_ptr) {
472     					hscx_fill_fifo(ch->twin);
473     				} else {
474     					clear_bit(0, &twin_hw->txbusy);
475     					ch->LINE_tx(ch->twin);
476     				}
477     			}
478     		}
479     	}
480     
481     	restore_flags(flags);
482     	return;
483     }
484     
485     static int MIXCOM_open(struct net_device *dev)
486     {
487     	struct comx_channel *ch = dev->priv;
488     	struct mixcom_privdata *hw = ch->HW_privdata;
489     	struct proc_dir_entry *procfile = ch->procdir->subdir;
490     	unsigned long flags; 
491     	int ret = -ENODEV;
492     
493     	if (!dev->base_addr || !dev->irq)
494     		goto err_ret;
495     
496     
497     	if(hw->channel==1) {
498     		if(!TWIN(dev) || !(COMX_CHANNEL(TWIN(dev))->init_status & 
499     		    IRQ_ALLOCATED)) {
500     			printk(KERN_ERR "%s: channel 0 not yet initialized\n",dev->name);
501     			ret = -EAGAIN;
502     			goto err_ret;
503     		}
504     	}
505     
506     
507     	/* Is our hw present at all ? Not checking for channel 0 if it is already 
508     	   open */
509     	if(hw->channel!=0 || !(ch->init_status & IRQ_ALLOCATED)) {
510     		if (!request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name)) {
511     			ret = -EAGAIN;
512     			goto err_ret;
513     		}
514     		if (mixcom_probe(dev)) {
515     			ret = -ENODEV;
516     			goto err_release_region;
517     		}
518     	}
519     
520     	if(hw->channel==0 && !(ch->init_status & IRQ_ALLOCATED)) {
521     		if (request_irq(dev->irq, MIXCOM_interrupt, 0, 
522     		    dev->name, (void *)dev)) {
523     			printk(KERN_ERR "MIXCOM: unable to obtain irq %d\n", dev->irq);
524     			ret = -EAGAIN;
525     			goto err_release_region;
526     		}
527     	}
528     
529     	save_flags(flags); cli();
530     
531     	if(hw->channel==0 && !(ch->init_status & IRQ_ALLOCATED)) {
532     		ch->init_status|=IRQ_ALLOCATED;
533     		mixcom_board_on(dev);
534     	}
535     
536     	mixcom_on(dev);
537     
538     
539     	hw->status=inb(MIXCOM_BOARD_BASE(dev) + MIXCOM_STATUS_OFFSET);
540     	if(hw->status != 0xff) {
541     		printk(KERN_DEBUG "%s: board has status register, good\n", dev->name);
542     		hw->card_has_status=1;
543     	}
544     
545     	hw->txbusy = 0;
546     	ch->init_status |= HW_OPEN;
547     	
548     	if (rd_hscx(dev, HSCX_STAR) & HSCX_CTS) {
549     		ch->line_status |= LINE_UP;
550     	} else {
551     		ch->line_status &= ~LINE_UP;
552     	}
553     
554     	restore_flags(flags);
555     
556     	ch->LINE_status(dev, ch->line_status);
557     
558     	for (; procfile ; procfile = procfile->next) {
559     		if (strcmp(procfile->name, FILENAME_IO) == 0 ||
560     		    strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
561     		    strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
562     		    strcmp(procfile->name, FILENAME_IRQ) == 0) {
563     			procfile->mode = S_IFREG |  0444;
564     		}
565     	}
566     
567     	return 0;
568     	
569     err_restore_flags:
570     	restore_flags(flags);
571     err_release_region:
572     	release_region(dev->base_addr, MIXCOM_IO_EXTENT);
573     err_ret:
574     	return ret;
575     }
576     
577     static int MIXCOM_close(struct net_device *dev)
578     {
579     	struct comx_channel *ch = dev->priv;
580     	struct mixcom_privdata *hw = ch->HW_privdata;
581     	struct proc_dir_entry *procfile = ch->procdir->subdir;
582     	unsigned long flags;
583     
584     
585     	save_flags(flags); cli();
586     
587     	mixcom_off(dev);
588     
589     	/* This is channel 0, twin is not open, we can safely turn off everything */
590     	if(hw->channel==0 && (!(TWIN(dev)) || 
591     	    !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN))) {
592     		mixcom_board_off(dev);
593     		free_irq(dev->irq, dev);
594     		release_region(dev->base_addr, MIXCOM_IO_EXTENT);
595     		ch->init_status &= ~IRQ_ALLOCATED;
596     	}
597     
598     	/* This is channel 1, channel 0 has already been shutdown, we can release
599     	   this one too */
600     	if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
601     		if(COMX_CHANNEL(TWIN(dev))->init_status & IRQ_ALLOCATED) {
602     			mixcom_board_off(TWIN(dev));
603     			free_irq(TWIN(dev)->irq, TWIN(dev));
604     			release_region(TWIN(dev)->base_addr, MIXCOM_IO_EXTENT);
605     			COMX_CHANNEL(TWIN(dev))->init_status &= ~IRQ_ALLOCATED;
606     		}
607     	}
608     
609     	/* the ioports for channel 1 can be safely released */
610     	if(hw->channel==1) {
611     		release_region(dev->base_addr, MIXCOM_IO_EXTENT);
612     	}
613     
614     	restore_flags(flags);
615     
616     	/* If we don't hold any hardware open */
617     	if(!(ch->init_status & IRQ_ALLOCATED)) {
618     		for (; procfile ; procfile = procfile->next) {
619     			if (strcmp(procfile->name, FILENAME_IO) == 0 ||
620     			    strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
621     			    strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
622     			    strcmp(procfile->name, FILENAME_IRQ) == 0) {
623     				procfile->mode = S_IFREG |  0644;
624     			}
625     		}
626     	}
627     
628     	/* channel 0 was only waiting for us to close channel 1 
629     	   close it completely */
630        
631     	if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
632     		for (procfile=COMX_CHANNEL(TWIN(dev))->procdir->subdir; 
633     		    procfile ; procfile = procfile->next) {
634     			if (strcmp(procfile->name, FILENAME_IO) == 0 ||
635     			    strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
636     			    strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
637     			    strcmp(procfile->name, FILENAME_IRQ) == 0) {
638     				procfile->mode = S_IFREG |  0644;
639     			}
640     		}
641     	}
642     	
643     	ch->init_status &= ~HW_OPEN;
644     	return 0;
645     }
646     
647     static int MIXCOM_statistics(struct net_device *dev,char *page)
648     {
649     	struct comx_channel *ch = dev->priv;
650     	// struct mixcom_privdata *hw = ch->HW_privdata;
651     	int len = 0;
652     
653     	if(ch->init_status && IRQ_ALLOCATED) {
654     		len += sprintf(page + len, "Mixcom board: hardware open\n");
655     	}
656     
657     	return len;
658     }
659     
660     static int MIXCOM_dump(struct net_device *dev) {
661     	return 0;
662     }
663     
664     static int mixcom_read_proc(char *page, char **start, off_t off, int count,
665     	int *eof, void *data)
666     {
667     	struct proc_dir_entry *file = (struct proc_dir_entry *)data;
668     	struct net_device *dev = file->parent->data;
669     	struct comx_channel *ch = dev->priv;
670     	struct mixcom_privdata *hw = ch->HW_privdata;
671     	int len = 0;
672     
673     	if (strcmp(file->name, FILENAME_IO) == 0) {
674     		len = sprintf(page, "0x%x\n", 
675     			(unsigned int)MIXCOM_BOARD_BASE(dev));
676     	} else if (strcmp(file->name, FILENAME_IRQ) == 0) {
677     		len = sprintf(page, "%d\n", (unsigned int)dev->irq);
678     	} else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
679     		if (hw->clock) len = sprintf(page, "%d\n", hw->clock);
680     			else len = sprintf(page, "external\n");
681     	} else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
682     		len = sprintf(page, "%01d\n", hw->channel);
683     	} else if (strcmp(file->name, FILENAME_TWIN) == 0) {
684     		if (ch->twin) {
685     			len = sprintf(page, "%s\n",ch->twin->name);
686     		} else {
687     			len = sprintf(page, "none\n");
688     		}
689     	} else {
690     		printk(KERN_ERR "mixcom_read_proc: internal error, filename %s\n", file->name);
691     		return -EBADF;
692     	}
693     
694     	if (off >= len) {
695     		*eof = 1;
696     		return 0;
697     	}
698     	*start = page + off;
699     	if (count >= len - off) *eof = 1;
700     	return min_t(int, count, len - off);
701     }
702     
703     
704     static struct net_device *mixcom_twin_check(struct net_device *dev)
705     {
706     	struct comx_channel *ch = dev->priv;
707     	struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
708     	struct mixcom_privdata *hw = ch->HW_privdata;
709     
710     	struct net_device *twin;
711     	struct comx_channel *ch_twin;
712     	struct mixcom_privdata *hw_twin;
713     
714     
715     	for ( ; procfile ; procfile = procfile->next) {
716     		if(!S_ISDIR(procfile->mode)) continue;
717                     
718             	twin = procfile->data;
719     	        ch_twin = twin->priv;
720             	hw_twin = ch_twin->HW_privdata;
721     
722     
723     	        if (twin != dev && dev->irq && dev->base_addr && 
724             	    dev->irq == twin->irq && 
725             	    ch->hardware == ch_twin->hardware &&
726     		    dev->base_addr == twin->base_addr + 
727     		    (1-2*hw->channel)*MIXCOM_CHANNEL_OFFSET &&
728     		    hw->channel == (1 - hw_twin->channel)) {
729     	        	if  (!TWIN(twin) || TWIN(twin)==dev) {
730     	        		return twin;
731     	        	}
732     		}
733             }
734     	return NULL;
735     }
736     
737     
738     static void setup_twin(struct net_device* dev) 
739     {
740     
741     	if(TWIN(dev) && TWIN(TWIN(dev))) {
742     		TWIN(TWIN(dev))=NULL;
743     	}
744     	if ((TWIN(dev) = mixcom_twin_check(dev)) != NULL) {
745     		if (TWIN(TWIN(dev)) && TWIN(TWIN(dev)) != dev) {
746     			TWIN(dev)=NULL;
747     		} else {
748     			TWIN(TWIN(dev))=dev;
749     		}
750     	}	
751     }
752     
753     static int mixcom_write_proc(struct file *file, const char *buffer,
754     	u_long count, void *data)
755     {
756     	struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
757     	struct net_device *dev = (struct net_device *)entry->parent->data;
758     	struct comx_channel *ch = dev->priv;
759     	struct mixcom_privdata *hw = ch->HW_privdata;
760     	char *page;
761     	int value;
762     
763     	if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
764     		return -ENOMEM;
765     	}
766     
767     	copy_from_user(page, buffer, count = min_t(unsigned long, count, PAGE_SIZE));
768     	if (*(page + count - 1) == '\n') {
769     		*(page + count - 1) = 0;
770     	}
771     
772     	if (strcmp(entry->name, FILENAME_IO) == 0) {
773     		value = simple_strtoul(page, NULL, 0);
774     		if (value != 0x180 && value != 0x280 && value != 0x380) {
775     			printk(KERN_ERR "MIXCOM: incorrect io address!\n");
776     		} else {
777     			dev->base_addr = MIXCOM_DEV_BASE(value,hw->channel);
778     		}
779     	} else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
780     		value = simple_strtoul(page, NULL, 0); 
781     		if (value < 0 || value > 15 || mixcom_set_irq[value]==0xFF) {
782     			printk(KERN_ERR "MIXCOM: incorrect irq value!\n");
783     		} else {
784     			dev->irq = value;	
785     		}
786     	} else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
787     		if (strncmp("ext", page, 3) == 0) {
788     			hw->clock = 0;
789     		} else {
790     			int kbps;
791     
792     			kbps = simple_strtoul(page, NULL, 0);
793     			if (!kbps) {
794     				hw->clock = 0;
795     			} else {
796     				hw->clock = kbps;
797     			}
798     			if (hw->clock < 32 || hw->clock > 2000) {
799     				hw->clock = 0;
800     				printk(KERN_ERR "MIXCOM: invalid clock rate!\n");
801     			}
802     		}
803     		if (ch->init_status & HW_OPEN && ch->HW_set_clock) {
804     			ch->HW_set_clock(dev);
805     		}
806     	} else if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
807     		value = simple_strtoul(page, NULL, 0);
808             	if (value > 2) {
809                     	printk(KERN_ERR "Invalid channel number\n");
810     	        } else {
811             		dev->base_addr+=(hw->channel - value) * MIXCOM_CHANNEL_OFFSET;
812     	        	hw->channel = value;
813     		}	        
814     	} else {
815     		printk(KERN_ERR "hw_read_proc: internal error, filename %s\n", 
816     			entry->name);
817     		return -EBADF;
818     	}
819     
820     	setup_twin(dev);
821     
822     	free_page((unsigned long)page);
823     	return count;
824     }
825     
826     static int MIXCOM_init(struct net_device *dev) {
827     	struct comx_channel *ch = dev->priv;
828     	struct mixcom_privdata *hw;
829     	struct proc_dir_entry *new_file;
830     
831     	if ((ch->HW_privdata = kmalloc(sizeof(struct mixcom_privdata), 
832     	    GFP_KERNEL)) == NULL) {
833     	    	return -ENOMEM;
834     	}
835     
836     	memset(hw = ch->HW_privdata, 0, sizeof(struct mixcom_privdata));
837     
838     	if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, 
839     	    ch->procdir)) == NULL) {
840     		goto cleanup_HW_privdata;
841     	}
842     	new_file->data = (void *)new_file;
843     	new_file->read_proc = &mixcom_read_proc;
844     	new_file->write_proc = &mixcom_write_proc;
845     	new_file->nlink = 1;
846     
847     	if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, 
848     	    ch->procdir)) == NULL) {
849     	    	goto cleanup_filename_io;
850     	}
851     	new_file->data = (void *)new_file;
852     	new_file->read_proc = &mixcom_read_proc;
853     	new_file->write_proc = &mixcom_write_proc;
854     	new_file->nlink = 1;
855     
856     #if 0
857     	if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, 
858     	    ch->procdir)) == NULL) {
859     	    	return -EIO;
860     	}
861     	new_file->data = (void *)new_file;
862     	new_file->read_proc = &mixcom_read_proc;
863     	new_file->write_proc = &mixcom_write_proc;
864     	new_file->nlink = 1;
865     #endif
866     
867     	if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644, 
868     	    ch->procdir)) == NULL) {
869     	    	goto cleanup_filename_irq;
870     	}
871     	new_file->data = (void *)new_file;
872     	new_file->read_proc = &mixcom_read_proc;
873     	new_file->write_proc = &mixcom_write_proc;
874     	new_file->nlink = 1;
875     
876     	if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, 
877     	    ch->procdir)) == NULL) {
878     	    	goto cleanup_filename_channel;
879     	}
880     	new_file->data = (void *)new_file;
881     	new_file->read_proc = &mixcom_read_proc;
882     	new_file->write_proc = &mixcom_write_proc;
883     	new_file->nlink = 1;
884     
885     	setup_twin(dev);
886     
887     	/* Fill in ch_struct hw specific pointers */
888     	ch->HW_access_board = NULL;
889     	ch->HW_release_board = NULL;
890     	ch->HW_txe = MIXCOM_txe;
891     	ch->HW_open = MIXCOM_open;
892     	ch->HW_close = MIXCOM_close;
893     	ch->HW_send_packet = MIXCOM_send_packet;
894     	ch->HW_statistics = MIXCOM_statistics;
895     	ch->HW_set_clock = NULL;
896     
897     	dev->base_addr = MIXCOM_DEV_BASE(MIXCOM_DEFAULT_IO,0);
898     	dev->irq = MIXCOM_DEFAULT_IRQ;
899     
900     	MOD_INC_USE_COUNT;
901     	return 0;
902     cleanup_filename_channel:
903     	remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
904     cleanup_filename_irq:
905     	remove_proc_entry(FILENAME_IRQ, ch->procdir);
906     cleanup_filename_io:
907     	remove_proc_entry(FILENAME_IO, ch->procdir);
908     cleanup_HW_privdata:
909     	kfree(ch->HW_privdata);
910     	return -EIO;
911     }
912     
913     static int MIXCOM_exit(struct net_device *dev)
914     {
915     	struct comx_channel *ch = dev->priv;
916     	struct mixcom_privdata *hw = ch->HW_privdata;
917     
918     	if(hw->channel==0 && TWIN(dev)) {
919     		return -EBUSY;
920     	}
921     
922     	if(hw->channel==1 && TWIN(dev)) {
923     		TWIN(TWIN(dev))=NULL;
924     	}
925     
926     	kfree(ch->HW_privdata);
927     	remove_proc_entry(FILENAME_IO, ch->procdir);
928     	remove_proc_entry(FILENAME_IRQ, ch->procdir);
929     #if 0
930     	remove_proc_entry(FILENAME_CLOCK, ch->procdir);
931     #endif
932     	remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
933     	remove_proc_entry(FILENAME_TWIN, ch->procdir);
934     
935     	MOD_DEC_USE_COUNT;
936     	return 0;
937     }
938     
939     static struct comx_hardware mixcomhw = {
940     	"mixcom",
941     	VERSION,
942     	MIXCOM_init, 
943     	MIXCOM_exit,
944     	MIXCOM_dump,
945     	NULL
946     };
947     	
948     /* Module management */
949     
950     #ifdef MODULE
951     #define comx_hw_mixcom_init init_module
952     #endif
953     
954     int __init comx_hw_mixcom_init(void)
955     {
956     	return(comx_register_hardware(&mixcomhw));
957     }
958     
959     #ifdef MODULE
960     void
961     cleanup_module(void)
962     {
963     	comx_unregister_hardware("mixcom");
964     }
965     #endif
966