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