This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / 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/interrupt.h>
43 #include <linux/module.h>
44 #include <linux/types.h>
45 #include <linux/netdevice.h>
46 #include <linux/proc_fs.h>
47 #include <linux/ioport.h>
48 #include <linux/delay.h>
49 #include <linux/init.h>
50
51 #include <asm/types.h>
52 #include <asm/uaccess.h>
53 #include <asm/io.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             time_before(jiffies, jiffs + HZ)) {
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 irqreturn_t 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 IRQ_NONE;
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 IRQ_HANDLED;
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_release_region:
570         release_region(dev->base_addr, MIXCOM_IO_EXTENT);
571 err_ret:
572         return ret;
573 }
574
575 static int MIXCOM_close(struct net_device *dev)
576 {
577         struct comx_channel *ch = dev->priv;
578         struct mixcom_privdata *hw = ch->HW_privdata;
579         struct proc_dir_entry *procfile = ch->procdir->subdir;
580         unsigned long flags;
581
582
583         save_flags(flags); cli();
584
585         mixcom_off(dev);
586
587         /* This is channel 0, twin is not open, we can safely turn off everything */
588         if(hw->channel==0 && (!(TWIN(dev)) || 
589             !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN))) {
590                 mixcom_board_off(dev);
591                 free_irq(dev->irq, dev);
592                 release_region(dev->base_addr, MIXCOM_IO_EXTENT);
593                 ch->init_status &= ~IRQ_ALLOCATED;
594         }
595
596         /* This is channel 1, channel 0 has already been shutdown, we can release
597            this one too */
598         if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
599                 if(COMX_CHANNEL(TWIN(dev))->init_status & IRQ_ALLOCATED) {
600                         mixcom_board_off(TWIN(dev));
601                         free_irq(TWIN(dev)->irq, TWIN(dev));
602                         release_region(TWIN(dev)->base_addr, MIXCOM_IO_EXTENT);
603                         COMX_CHANNEL(TWIN(dev))->init_status &= ~IRQ_ALLOCATED;
604                 }
605         }
606
607         /* the ioports for channel 1 can be safely released */
608         if(hw->channel==1) {
609                 release_region(dev->base_addr, MIXCOM_IO_EXTENT);
610         }
611
612         restore_flags(flags);
613
614         /* If we don't hold any hardware open */
615         if(!(ch->init_status & IRQ_ALLOCATED)) {
616                 for (; procfile ; procfile = procfile->next) {
617                         if (strcmp(procfile->name, FILENAME_IO) == 0 ||
618                             strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
619                             strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
620                             strcmp(procfile->name, FILENAME_IRQ) == 0) {
621                                 procfile->mode = S_IFREG |  0644;
622                         }
623                 }
624         }
625
626         /* channel 0 was only waiting for us to close channel 1 
627            close it completely */
628    
629         if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
630                 for (procfile=COMX_CHANNEL(TWIN(dev))->procdir->subdir; 
631                     procfile ; procfile = procfile->next) {
632                         if (strcmp(procfile->name, FILENAME_IO) == 0 ||
633                             strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
634                             strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
635                             strcmp(procfile->name, FILENAME_IRQ) == 0) {
636                                 procfile->mode = S_IFREG |  0644;
637                         }
638                 }
639         }
640         
641         ch->init_status &= ~HW_OPEN;
642         return 0;
643 }
644
645 static int MIXCOM_statistics(struct net_device *dev,char *page)
646 {
647         struct comx_channel *ch = dev->priv;
648         // struct mixcom_privdata *hw = ch->HW_privdata;
649         int len = 0;
650
651         if(ch->init_status && IRQ_ALLOCATED) {
652                 len += sprintf(page + len, "Mixcom board: hardware open\n");
653         }
654
655         return len;
656 }
657
658 static int MIXCOM_dump(struct net_device *dev) {
659         return 0;
660 }
661
662 static int mixcom_read_proc(char *page, char **start, off_t off, int count,
663         int *eof, void *data)
664 {
665         struct proc_dir_entry *file = (struct proc_dir_entry *)data;
666         struct net_device *dev = file->parent->data;
667         struct comx_channel *ch = dev->priv;
668         struct mixcom_privdata *hw = ch->HW_privdata;
669         int len = 0;
670
671         if (strcmp(file->name, FILENAME_IO) == 0) {
672                 len = sprintf(page, "0x%x\n", 
673                         (unsigned int)MIXCOM_BOARD_BASE(dev));
674         } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
675                 len = sprintf(page, "%d\n", (unsigned int)dev->irq);
676         } else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
677                 if (hw->clock) len = sprintf(page, "%d\n", hw->clock);
678                         else len = sprintf(page, "external\n");
679         } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
680                 len = sprintf(page, "%01d\n", hw->channel);
681         } else if (strcmp(file->name, FILENAME_TWIN) == 0) {
682                 if (ch->twin) {
683                         len = sprintf(page, "%s\n",ch->twin->name);
684                 } else {
685                         len = sprintf(page, "none\n");
686                 }
687         } else {
688                 printk(KERN_ERR "mixcom_read_proc: internal error, filename %s\n", file->name);
689                 return -EBADF;
690         }
691
692         if (off >= len) {
693                 *eof = 1;
694                 return 0;
695         }
696         *start = page + off;
697         if (count >= len - off) *eof = 1;
698         return min_t(int, count, len - off);
699 }
700
701
702 static struct net_device *mixcom_twin_check(struct net_device *dev)
703 {
704         struct comx_channel *ch = dev->priv;
705         struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
706         struct mixcom_privdata *hw = ch->HW_privdata;
707
708         struct net_device *twin;
709         struct comx_channel *ch_twin;
710         struct mixcom_privdata *hw_twin;
711
712
713         for ( ; procfile ; procfile = procfile->next) {
714                 if(!S_ISDIR(procfile->mode)) continue;
715                 
716                 twin = procfile->data;
717                 ch_twin = twin->priv;
718                 hw_twin = ch_twin->HW_privdata;
719
720
721                 if (twin != dev && dev->irq && dev->base_addr && 
722                     dev->irq == twin->irq && 
723                     ch->hardware == ch_twin->hardware &&
724                     dev->base_addr == twin->base_addr + 
725                     (1-2*hw->channel)*MIXCOM_CHANNEL_OFFSET &&
726                     hw->channel == (1 - hw_twin->channel)) {
727                         if  (!TWIN(twin) || TWIN(twin)==dev) {
728                                 return twin;
729                         }
730                 }
731         }
732         return NULL;
733 }
734
735
736 static void setup_twin(struct net_device* dev) 
737 {
738
739         if(TWIN(dev) && TWIN(TWIN(dev))) {
740                 TWIN(TWIN(dev))=NULL;
741         }
742         if ((TWIN(dev) = mixcom_twin_check(dev)) != NULL) {
743                 if (TWIN(TWIN(dev)) && TWIN(TWIN(dev)) != dev) {
744                         TWIN(dev)=NULL;
745                 } else {
746                         TWIN(TWIN(dev))=dev;
747                 }
748         }       
749 }
750
751 static int mixcom_write_proc(struct file *file, const char *buffer,
752         u_long count, void *data)
753 {
754         struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
755         struct net_device *dev = (struct net_device *)entry->parent->data;
756         struct comx_channel *ch = dev->priv;
757         struct mixcom_privdata *hw = ch->HW_privdata;
758         char *page;
759         int value;
760
761         if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
762                 return -ENOMEM;
763         }
764
765         if (copy_from_user(page, buffer, count = min_t(unsigned long, count, PAGE_SIZE))) {
766                 free_page((unsigned long)page);
767                 return -EFAULT;
768         }
769         if (*(page + count - 1) == '\n') {
770                 *(page + count - 1) = 0;
771         }
772
773         if (strcmp(entry->name, FILENAME_IO) == 0) {
774                 value = simple_strtoul(page, NULL, 0);
775                 if (value != 0x180 && value != 0x280 && value != 0x380) {
776                         printk(KERN_ERR "MIXCOM: incorrect io address!\n");
777                 } else {
778                         dev->base_addr = MIXCOM_DEV_BASE(value,hw->channel);
779                 }
780         } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
781                 value = simple_strtoul(page, NULL, 0); 
782                 if (value < 0 || value > 15 || mixcom_set_irq[value]==0xFF) {
783                         printk(KERN_ERR "MIXCOM: incorrect irq value!\n");
784                 } else {
785                         dev->irq = value;       
786                 }
787         } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
788                 if (strncmp("ext", page, 3) == 0) {
789                         hw->clock = 0;
790                 } else {
791                         int kbps;
792
793                         kbps = simple_strtoul(page, NULL, 0);
794                         if (!kbps) {
795                                 hw->clock = 0;
796                         } else {
797                                 hw->clock = kbps;
798                         }
799                         if (hw->clock < 32 || hw->clock > 2000) {
800                                 hw->clock = 0;
801                                 printk(KERN_ERR "MIXCOM: invalid clock rate!\n");
802                         }
803                 }
804                 if (ch->init_status & HW_OPEN && ch->HW_set_clock) {
805                         ch->HW_set_clock(dev);
806                 }
807         } else if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
808                 value = simple_strtoul(page, NULL, 0);
809                 if (value > 2) {
810                         printk(KERN_ERR "Invalid channel number\n");
811                 } else {
812                         dev->base_addr+=(hw->channel - value) * MIXCOM_CHANNEL_OFFSET;
813                         hw->channel = value;
814                 }               
815         } else {
816                 printk(KERN_ERR "hw_read_proc: internal error, filename %s\n", 
817                         entry->name);
818                 return -EBADF;
819         }
820
821         setup_twin(dev);
822
823         free_page((unsigned long)page);
824         return count;
825 }
826
827 static int MIXCOM_init(struct net_device *dev) {
828         struct comx_channel *ch = dev->priv;
829         struct mixcom_privdata *hw;
830         struct proc_dir_entry *new_file;
831
832         if ((ch->HW_privdata = kmalloc(sizeof(struct mixcom_privdata), 
833             GFP_KERNEL)) == NULL) {
834                 return -ENOMEM;
835         }
836
837         memset(hw = ch->HW_privdata, 0, sizeof(struct mixcom_privdata));
838
839         if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, 
840             ch->procdir)) == NULL) {
841                 goto cleanup_HW_privdata;
842         }
843         new_file->data = (void *)new_file;
844         new_file->read_proc = &mixcom_read_proc;
845         new_file->write_proc = &mixcom_write_proc;
846         new_file->nlink = 1;
847
848         if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, 
849             ch->procdir)) == NULL) {
850                 goto cleanup_filename_io;
851         }
852         new_file->data = (void *)new_file;
853         new_file->read_proc = &mixcom_read_proc;
854         new_file->write_proc = &mixcom_write_proc;
855         new_file->nlink = 1;
856
857 #if 0
858         if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, 
859             ch->procdir)) == NULL) {
860                 return -EIO;
861         }
862         new_file->data = (void *)new_file;
863         new_file->read_proc = &mixcom_read_proc;
864         new_file->write_proc = &mixcom_write_proc;
865         new_file->nlink = 1;
866 #endif
867
868         if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644, 
869             ch->procdir)) == NULL) {
870                 goto cleanup_filename_irq;
871         }
872         new_file->data = (void *)new_file;
873         new_file->read_proc = &mixcom_read_proc;
874         new_file->write_proc = &mixcom_write_proc;
875         new_file->nlink = 1;
876
877         if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, 
878             ch->procdir)) == NULL) {
879                 goto cleanup_filename_channel;
880         }
881         new_file->data = (void *)new_file;
882         new_file->read_proc = &mixcom_read_proc;
883         new_file->write_proc = &mixcom_write_proc;
884         new_file->nlink = 1;
885
886         setup_twin(dev);
887
888         /* Fill in ch_struct hw specific pointers */
889         ch->HW_access_board = NULL;
890         ch->HW_release_board = NULL;
891         ch->HW_txe = MIXCOM_txe;
892         ch->HW_open = MIXCOM_open;
893         ch->HW_close = MIXCOM_close;
894         ch->HW_send_packet = MIXCOM_send_packet;
895         ch->HW_statistics = MIXCOM_statistics;
896         ch->HW_set_clock = NULL;
897
898         dev->base_addr = MIXCOM_DEV_BASE(MIXCOM_DEFAULT_IO,0);
899         dev->irq = MIXCOM_DEFAULT_IRQ;
900
901         MOD_INC_USE_COUNT;
902         return 0;
903 cleanup_filename_channel:
904         remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
905 cleanup_filename_irq:
906         remove_proc_entry(FILENAME_IRQ, ch->procdir);
907 cleanup_filename_io:
908         remove_proc_entry(FILENAME_IO, ch->procdir);
909 cleanup_HW_privdata:
910         kfree(ch->HW_privdata);
911         return -EIO;
912 }
913
914 static int MIXCOM_exit(struct net_device *dev)
915 {
916         struct comx_channel *ch = dev->priv;
917         struct mixcom_privdata *hw = ch->HW_privdata;
918
919         if(hw->channel==0 && TWIN(dev)) {
920                 return -EBUSY;
921         }
922
923         if(hw->channel==1 && TWIN(dev)) {
924                 TWIN(TWIN(dev))=NULL;
925         }
926
927         kfree(ch->HW_privdata);
928         remove_proc_entry(FILENAME_IO, ch->procdir);
929         remove_proc_entry(FILENAME_IRQ, ch->procdir);
930 #if 0
931         remove_proc_entry(FILENAME_CLOCK, ch->procdir);
932 #endif
933         remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
934         remove_proc_entry(FILENAME_TWIN, ch->procdir);
935
936         MOD_DEC_USE_COUNT;
937         return 0;
938 }
939
940 static struct comx_hardware mixcomhw = {
941         "mixcom",
942         VERSION,
943         MIXCOM_init, 
944         MIXCOM_exit,
945         MIXCOM_dump,
946         NULL
947 };
948         
949 static int __init comx_hw_mixcom_init(void)
950 {
951         return comx_register_hardware(&mixcomhw);
952 }
953
954 static void __exit comx_hw_mixcom_exit(void)
955 {
956         comx_unregister_hardware("mixcom");
957 }
958
959 module_init(comx_hw_mixcom_init);
960 module_exit(comx_hw_mixcom_exit);