OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [net/] [wan/] [comx-hw-mixcom.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
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_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
        copy_from_user(page, buffer, count = min_t(unsigned long, count, PAGE_SIZE));
766
        if (*(page + count - 1) == '\n') {
767
                *(page + count - 1) = 0;
768
        }
769
 
770
        if (strcmp(entry->name, FILENAME_IO) == 0) {
771
                value = simple_strtoul(page, NULL, 0);
772
                if (value != 0x180 && value != 0x280 && value != 0x380) {
773
                        printk(KERN_ERR "MIXCOM: incorrect io address!\n");
774
                } else {
775
                        dev->base_addr = MIXCOM_DEV_BASE(value,hw->channel);
776
                }
777
        } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
778
                value = simple_strtoul(page, NULL, 0);
779
                if (value < 0 || value > 15 || mixcom_set_irq[value]==0xFF) {
780
                        printk(KERN_ERR "MIXCOM: incorrect irq value!\n");
781
                } else {
782
                        dev->irq = value;
783
                }
784
        } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
785
                if (strncmp("ext", page, 3) == 0) {
786
                        hw->clock = 0;
787
                } else {
788
                        int kbps;
789
 
790
                        kbps = simple_strtoul(page, NULL, 0);
791
                        if (!kbps) {
792
                                hw->clock = 0;
793
                        } else {
794
                                hw->clock = kbps;
795
                        }
796
                        if (hw->clock < 32 || hw->clock > 2000) {
797
                                hw->clock = 0;
798
                                printk(KERN_ERR "MIXCOM: invalid clock rate!\n");
799
                        }
800
                }
801
                if (ch->init_status & HW_OPEN && ch->HW_set_clock) {
802
                        ch->HW_set_clock(dev);
803
                }
804
        } else if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
805
                value = simple_strtoul(page, NULL, 0);
806
                if (value > 2) {
807
                        printk(KERN_ERR "Invalid channel number\n");
808
                } else {
809
                        dev->base_addr+=(hw->channel - value) * MIXCOM_CHANNEL_OFFSET;
810
                        hw->channel = value;
811
                }
812
        } else {
813
                printk(KERN_ERR "hw_read_proc: internal error, filename %s\n",
814
                        entry->name);
815
                return -EBADF;
816
        }
817
 
818
        setup_twin(dev);
819
 
820
        free_page((unsigned long)page);
821
        return count;
822
}
823
 
824
static int MIXCOM_init(struct net_device *dev) {
825
        struct comx_channel *ch = dev->priv;
826
        struct mixcom_privdata *hw;
827
        struct proc_dir_entry *new_file;
828
 
829
        if ((ch->HW_privdata = kmalloc(sizeof(struct mixcom_privdata),
830
            GFP_KERNEL)) == NULL) {
831
                return -ENOMEM;
832
        }
833
 
834
        memset(hw = ch->HW_privdata, 0, sizeof(struct mixcom_privdata));
835
 
836
        if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644,
837
            ch->procdir)) == NULL) {
838
                goto cleanup_HW_privdata;
839
        }
840
        new_file->data = (void *)new_file;
841
        new_file->read_proc = &mixcom_read_proc;
842
        new_file->write_proc = &mixcom_write_proc;
843
        new_file->nlink = 1;
844
 
845
        if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644,
846
            ch->procdir)) == NULL) {
847
                goto cleanup_filename_io;
848
        }
849
        new_file->data = (void *)new_file;
850
        new_file->read_proc = &mixcom_read_proc;
851
        new_file->write_proc = &mixcom_write_proc;
852
        new_file->nlink = 1;
853
 
854
#if 0
855
        if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644,
856
            ch->procdir)) == NULL) {
857
                return -EIO;
858
        }
859
        new_file->data = (void *)new_file;
860
        new_file->read_proc = &mixcom_read_proc;
861
        new_file->write_proc = &mixcom_write_proc;
862
        new_file->nlink = 1;
863
#endif
864
 
865
        if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644,
866
            ch->procdir)) == NULL) {
867
                goto cleanup_filename_irq;
868
        }
869
        new_file->data = (void *)new_file;
870
        new_file->read_proc = &mixcom_read_proc;
871
        new_file->write_proc = &mixcom_write_proc;
872
        new_file->nlink = 1;
873
 
874
        if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444,
875
            ch->procdir)) == NULL) {
876
                goto cleanup_filename_channel;
877
        }
878
        new_file->data = (void *)new_file;
879
        new_file->read_proc = &mixcom_read_proc;
880
        new_file->write_proc = &mixcom_write_proc;
881
        new_file->nlink = 1;
882
 
883
        setup_twin(dev);
884
 
885
        /* Fill in ch_struct hw specific pointers */
886
        ch->HW_access_board = NULL;
887
        ch->HW_release_board = NULL;
888
        ch->HW_txe = MIXCOM_txe;
889
        ch->HW_open = MIXCOM_open;
890
        ch->HW_close = MIXCOM_close;
891
        ch->HW_send_packet = MIXCOM_send_packet;
892
        ch->HW_statistics = MIXCOM_statistics;
893
        ch->HW_set_clock = NULL;
894
 
895
        dev->base_addr = MIXCOM_DEV_BASE(MIXCOM_DEFAULT_IO,0);
896
        dev->irq = MIXCOM_DEFAULT_IRQ;
897
 
898
        MOD_INC_USE_COUNT;
899
        return 0;
900
cleanup_filename_channel:
901
        remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
902
cleanup_filename_irq:
903
        remove_proc_entry(FILENAME_IRQ, ch->procdir);
904
cleanup_filename_io:
905
        remove_proc_entry(FILENAME_IO, ch->procdir);
906
cleanup_HW_privdata:
907
        kfree(ch->HW_privdata);
908
        return -EIO;
909
}
910
 
911
static int MIXCOM_exit(struct net_device *dev)
912
{
913
        struct comx_channel *ch = dev->priv;
914
        struct mixcom_privdata *hw = ch->HW_privdata;
915
 
916
        if(hw->channel==0 && TWIN(dev)) {
917
                return -EBUSY;
918
        }
919
 
920
        if(hw->channel==1 && TWIN(dev)) {
921
                TWIN(TWIN(dev))=NULL;
922
        }
923
 
924
        kfree(ch->HW_privdata);
925
        remove_proc_entry(FILENAME_IO, ch->procdir);
926
        remove_proc_entry(FILENAME_IRQ, ch->procdir);
927
#if 0
928
        remove_proc_entry(FILENAME_CLOCK, ch->procdir);
929
#endif
930
        remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
931
        remove_proc_entry(FILENAME_TWIN, ch->procdir);
932
 
933
        MOD_DEC_USE_COUNT;
934
        return 0;
935
}
936
 
937
static struct comx_hardware mixcomhw = {
938
        "mixcom",
939
        VERSION,
940
        MIXCOM_init,
941
        MIXCOM_exit,
942
        MIXCOM_dump,
943
        NULL
944
};
945
 
946
/* Module management */
947
 
948
#ifdef MODULE
949
#define comx_hw_mixcom_init init_module
950
#endif
951
 
952
int __init comx_hw_mixcom_init(void)
953
{
954
        return(comx_register_hardware(&mixcomhw));
955
}
956
 
957
#ifdef MODULE
958
void
959
cleanup_module(void)
960
{
961
        comx_unregister_hardware("mixcom");
962
}
963
#endif

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.