aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/irda/ksdazzle-sir.c
blob: 64df27f2bfd469cb2568312ddbd5e9ef028280e7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
/*****************************************************************************
*
* Filename:      ksdazzle.c
* Version:       0.1.2
* Description:   Irda KingSun Dazzle USB Dongle
* Status:        Experimental
* Author:        Alex Villacís Lasso <a_villacis@palosanto.com>
*
*    Based on stir4200, mcs7780, kingsun-sir drivers.
*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with this program; if not, write to the Free Software
*    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*****************************************************************************/

/*
 * Following is my most current (2007-07-26) understanding of how the Kingsun
 * 07D0:4100 dongle (sometimes known as the MA-660) is supposed to work. This
 * information was deduced by examining the USB traffic captured with USBSnoopy
 * from the WinXP driver. Feel free to update here as more of the dongle is
 * known.
 *
 * General: This dongle exposes one interface with two interrupt endpoints, one
 * IN and one OUT. In this regard, it is similar to what the Kingsun/Donshine
 * dongle (07c0:4200) exposes. Traffic is raw and needs to be wrapped and
 * unwrapped manually as in stir4200, kingsun-sir, and ks959-sir.
 *
 * Transmission: To transmit an IrDA frame, it is necessary to wrap it, then
 * split it into multiple segments of up to 7 bytes each, and transmit each in
 * sequence. It seems that sending a single big block (like kingsun-sir does)
 * won't work with this dongle. Each segment needs to be prefixed with a value
 * equal to (unsigned char)0xF8 + <number of bytes in segment>, inside a payload
 * of exactly 8 bytes. For example, a segment of 1 byte gets prefixed by 0xF9,
 * and one of 7 bytes gets prefixed by 0xFF. The bytes at the end of the
 * payload, not considered by the prefix, are ignored (set to 0 by this
 * implementation).
 *
 * Reception: To receive data, the driver must poll the dongle regularly (like
 * kingsun-sir.c) with interrupt URBs. If data is available, it will be returned
 * in payloads from 0 to 8 bytes long. When concatenated, these payloads form
 * a raw IrDA stream that needs to be unwrapped as in stir4200 and kingsun-sir
 *
 * Speed change: To change the speed of the dongle, the driver prepares a
 * control URB with the following as a setup packet:
 *    bRequestType    USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE
 *    bRequest        0x09
 *    wValue          0x0200
 *    wIndex          0x0001
 *    wLength         0x0008 (length of the payload)
 * The payload is a 8-byte record, apparently identical to the one used in
 * drivers/usb/serial/cypress_m8.c to change speed:
 *     __u32 baudSpeed;
 *    unsigned int dataBits : 2;    // 0 - 5 bits 3 - 8 bits
 *    unsigned int : 1;
 *    unsigned int stopBits : 1;
 *    unsigned int parityEnable : 1;
 *    unsigned int parityType : 1;
 *    unsigned int : 1;
 *    unsigned int reset : 1;
 *    unsigned char reserved[3];    // set to 0
 *
 * For now only SIR speeds have been observed with this dongle. Therefore,
 * nothing is known on what changes (if any) must be done to frame wrapping /
 * unwrapping for higher than SIR speeds. This driver assumes no change is
 * necessary and announces support for all the way to 115200 bps.
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kref.h>
#include <linux/usb.h>
#include <linux/device.h>
#include <linux/crc32.h>

#include <asm/unaligned.h>
#include <asm/byteorder.h>
#include <asm/uaccess.h>

#include <net/irda/irda.h>
#include <net/irda/wrapper.h>
#include <net/irda/crc.h>

#define KSDAZZLE_VENDOR_ID 0x07d0
#define KSDAZZLE_PRODUCT_ID 0x4100

/* These are the currently known USB ids */
static struct usb_device_id dongles[] = {
	/* KingSun Co,Ltd  IrDA/USB Bridge */
	{USB_DEVICE(KSDAZZLE_VENDOR_ID, KSDAZZLE_PRODUCT_ID)},
	{}
};

MODULE_DEVICE_TABLE(usb, dongles);

#define KINGSUN_MTT 0x07
#define KINGSUN_REQ_RECV 0x01
#define KINGSUN_REQ_SEND 0x09

#define KINGSUN_SND_FIFO_SIZE    2048	/* Max packet we can send */
#define KINGSUN_RCV_MAX 2048	/* Max transfer we can receive */

struct ksdazzle_speedparams {
	__le32 baudrate;	/* baud rate, little endian */
	__u8 flags;
	__u8 reserved[3];
} __attribute__ ((packed));

#define KS_DATA_5_BITS 0x00
#define KS_DATA_6_BITS 0x01
#define KS_DATA_7_BITS 0x02
#define KS_DATA_8_BITS 0x03

#define KS_STOP_BITS_1 0x00
#define KS_STOP_BITS_2 0x08

#define KS_PAR_DISABLE    0x00
#define KS_PAR_EVEN    0x10
#define KS_PAR_ODD    0x30
#define KS_RESET    0x80

#define KINGSUN_EP_IN			0
#define KINGSUN_EP_OUT			1

struct ksdazzle_cb {
	struct usb_device *usbdev;	/* init: probe_irda */
	struct net_device *netdev;	/* network layer */
	struct irlap_cb *irlap;	/* The link layer we are binded to */

	struct qos_info qos;

	struct urb *tx_urb;
	__u8 *tx_buf_clear;
	unsigned int tx_buf_clear_used;
	unsigned int tx_buf_clear_sent;
	__u8 tx_payload[8];

	struct urb *rx_urb;
	__u8 *rx_buf;
	iobuff_t rx_unwrap_buff;

	struct usb_ctrlrequest *speed_setuprequest;
	struct urb *speed_urb;
	struct ksdazzle_speedparams speedparams;
	unsigned int new_speed;

	__u8 ep_in;
	__u8 ep_out;

	spinlock_t lock;
	int receiving;
};

/* Callback transmission routine */
static void ksdazzle_speed_irq(struct urb *urb)
{
	/* unlink, shutdown, unplug, other nasties */
	if (urb->status != 0) {
		err("ksdazzle_speed_irq: urb asynchronously failed - %d",
		    urb->status);
	}
}

/* Send a control request to change speed of the dongle */
static int ksdazzle_change_speed(struct ksdazzle_cb *kingsun, unsigned speed)
{
	static unsigned int supported_speeds[] = { 2400, 9600, 19200, 38400,
		57600, 115200, 576000, 1152000, 4000000, 0
	};
	int err;
	unsigned int i;

	if (kingsun->speed_setuprequest == NULL || kingsun->speed_urb == NULL)
		return -ENOMEM;

	/* Check that requested speed is among the supported ones */
	for (i = 0; supported_speeds[i] && supported_speeds[i] != speed; i++) ;
	if (supported_speeds[i] == 0)
		return -EOPNOTSUPP;

	memset(&(kingsun->speedparams), 0, sizeof(struct ksdazzle_speedparams));
	kingsun->speedparams.baudrate = cpu_to_le32(speed);
	kingsun->speedparams.flags = KS_DATA_8_BITS;

	/* speed_setuprequest pre-filled in ksdazzle_probe */
	usb_fill_control_urb(kingsun->speed_urb, kingsun->usbdev,
			     usb_sndctrlpipe(kingsun->usbdev, 0),
			     (unsigned char *)kingsun->speed_setuprequest,
			     &(kingsun->speedparams),
			     sizeof(struct ksdazzle_speedparams),
			     ksdazzle_speed_irq, kingsun);
	kingsun->speed_urb->status = 0;
	err = usb_submit_urb(kingsun->speed_urb, GFP_ATOMIC);

	return err;
}

/* Submit one fragment of an IrDA frame to the dongle */
static void ksdazzle_send_irq(struct urb *urb);
static int ksdazzle_submit_tx_fragment(struct ksdazzle_cb *kingsun)
{
	unsigned int wraplen;
	int ret;

	/* We can send at most 7 bytes of payload at a time */
	wraplen = 7;
	if (wraplen > kingsun->tx_buf_clear_used)
		wraplen = kingsun->tx_buf_clear_used;

	/* Prepare payload prefix with used length */
	memset(kingsun->tx_payload, 0, 8);
	kingsun->tx_payload[0] = (unsigned char)0xf8 + wraplen;
	memcpy(kingsun->tx_payload + 1, kingsun->tx_buf_clear, wraplen);

	usb_fill_int_urb(kingsun->tx_urb, kingsun->usbdev,
			 usb_sndintpipe(kingsun->usbdev, kingsun->ep_out),
			 kingsun->tx_payload, 8, ksdazzle_send_irq, kingsun, 1);
	kingsun->tx_urb->status = 0;
	ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC);

	/* Remember how much data was sent, in order to update at callback */
	kingsun->tx_buf_clear_sent = (ret == 0) ? wraplen : 0;
	return ret;
}

/* Callback transmission routine */
static void ksdazzle_send_irq(struct urb *urb)
{
	struct ksdazzle_cb *kingsun = urb->context;
	struct net_device *netdev = kingsun->netdev;
	int ret = 0;

	/* in process of stopping, just drop data */
	if (!netif_running(kingsun->netdev)) {
		err("ksdazzle_send_irq: Network not running!");
		return;
	}

	/* unlink, shutdown, unplug, other nasties */
	if (urb->status != 0) {
		err("ksdazzle_send_irq: urb asynchronously failed - %d",
		    urb->status);
		return;
	}

	if (kingsun->tx_buf_clear_used > 0) {
		/* Update data remaining to be sent */
		if (kingsun->tx_buf_clear_sent < kingsun->tx_buf_clear_used) {
			memmove(kingsun->tx_buf_clear,
				kingsun->tx_buf_clear +
				kingsun->tx_buf_clear_sent,
				kingsun->tx_buf_clear_used -
				kingsun->tx_buf_clear_sent);
		}
		kingsun->tx_buf_clear_used -= kingsun->tx_buf_clear_sent;
		kingsun->tx_buf_clear_sent = 0;

		if (kingsun->tx_buf_clear_used > 0) {
			/* There is more data to be sent */
			if ((ret = ksdazzle_submit_tx_fragment(kingsun)) != 0) {
				err("ksdazzle_send_irq: failed tx_urb submit: %d", ret);
				switch (ret) {
				case -ENODEV:
				case -EPIPE:
					break;
				default:
					netdev->stats.tx_errors++;
					netif_start_queue(netdev);
				}
			}
		} else {
			/* All data sent, send next speed && wake network queue */
			if (kingsun->new_speed != -1 &&
			    cpu_to_le32(kingsun->new_speed) !=
			    kingsun->speedparams.baudrate)
				ksdazzle_change_speed(kingsun,
						      kingsun->new_speed);

			netif_wake_queue(netdev);
		}
	}
}

/*
 * Called from net/core when new frame is available.
 */
static int ksdazzle_hard_xmit(struct sk_buff *skb, struct net_device *netdev)
{
	struct ksdazzle_cb *kingsun;
	unsigned int wraplen;
	int ret = 0;

	if (skb == NULL || netdev == NULL)
		return -EINVAL;

	netif_stop_queue(netdev);

	/* the IRDA wrapping routines don't deal with non linear skb */
	SKB_LINEAR_ASSERT(skb);

	kingsun = netdev_priv(netdev);

	spin_lock(&kingsun->lock);
	kingsun->new_speed = irda_get_next_speed(skb);

	/* Append data to the end of whatever data remains to be transmitted */
	wraplen =
	    async_wrap_skb(skb, kingsun->tx_buf_clear, KINGSUN_SND_FIFO_SIZE);
	kingsun->tx_buf_clear_used = wraplen;

	if ((ret = ksdazzle_submit_tx_fragment(kingsun)) != 0) {
		err("ksdazzle_hard_xmit: failed tx_urb submit: %d", ret);
		switch (ret) {
		case -ENODEV:
		case -EPIPE:
			break;
		default:
			netdev->stats.tx_errors++;
			netif_start_queue(netdev);
		}
	} else {
		netdev->stats.tx_packets++;
		netdev->stats.tx_bytes += skb->len;

	}

	dev_kfree_skb(skb);
	spin_unlock(&kingsun->lock);

	return ret;
}

/* Receive callback function */
static void ksdazzle_rcv_irq(struct urb *urb)
{
	struct ksdazzle_cb *kingsun = urb->context;
	struct net_device *netdev = kingsun->netdev;

	/* in process of stopping, just drop data */
	if (!netif_running(netdev)) {
		kingsun->receiving = 0;
		return;
	}

	/* unlink, shutdown, unplug, other nasties */
	if (urb->status != 0) {
		err("ksdazzle_rcv_irq: urb asynchronously failed - %d",
		    urb->status);
		kingsun->receiving = 0;
		return;
	}

	if (urb->actual_length > 0) {
		__u8 *bytes = urb->transfer_buffer;
		unsigned int i;

		for (i = 0; i < urb->actual_length; i++) {
			async_unwrap_char(netdev, &netdev->stats,
					  &kingsun->rx_unwrap_buff, bytes[i]);
		}
		kingsun->receiving =
		    (kingsun->rx_unwrap_buff.state != OUTSIDE_FRAME) ? 1 : 0;
	}

	/* This urb has already been filled in ksdazzle_net_open. It is assumed that
	   urb keeps the pointer to the payload buffer.
	 */
	urb->status = 0;
	usb_submit_urb(urb, GFP_ATOMIC);
}

/*
 * Function ksdazzle_net_open (dev)
 *
 *    Network device is taken up. Usually this is done by "ifconfig irda0 up"
 */
static int ksdazzle_net_open(struct net_device *netdev)
{
	struct ksdazzle_cb *kingsun = netdev_priv(netdev);
	int err = -ENOMEM;
	char hwname[16];

	/* At this point, urbs are NULL, and skb is NULL (see ksdazzle_probe) */
	kingsun->receiving = 0;

	/* Initialize for SIR to copy data directly into skb.  */
	kingsun->rx_unwrap_buff.in_frame = FALSE;
	kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME;
	kingsun->rx_unwrap_buff.truesize = IRDA_SKB_MAX_MTU;
	kingsun->rx_unwrap_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU);
	if (!kingsun->rx_unwrap_buff.skb)
		goto free_mem;

	skb_reserve(kingsun->rx_unwrap_buff.skb, 1);
	kingsun->rx_unwrap_buff.head = kingsun->rx_unwrap_buff.skb->data;

	kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
	if (!kingsun->rx_urb)
		goto free_mem;

	kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
	if (!kingsun->tx_urb)
		goto free_mem;

	kingsun->speed_urb = usb_alloc_urb(0, GFP_KERNEL);
	if (!kingsun->speed_urb)
		goto free_mem;

	/* Initialize speed for dongle */
	kingsun->new_speed = 9600;
	err = ksdazzle_change_speed(kingsun, 9600);
	if (err < 0)
		goto free_mem;

	/*
	 * Now that everything should be initialized properly,
	 * Open new IrLAP layer instance to take care of us...
	 */
	sprintf(hwname, "usb#%d", kingsun->usbdev->devnum);
	kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname);
	if (!kingsun->irlap) {
		err("ksdazzle-sir: irlap_open failed");
		goto free_mem;
	}

	/* Start reception. */
	usb_fill_int_urb(kingsun->rx_urb, kingsun->usbdev,
			 usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in),
			 kingsun->rx_buf, KINGSUN_RCV_MAX, ksdazzle_rcv_irq,
			 kingsun, 1);
	kingsun->rx_urb->status = 0;
	err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL);
	if (err) {
		err("ksdazzle-sir: first urb-submit failed: %d", err);
		goto close_irlap;
	}

	netif_start_queue(netdev);

	/* Situation at this point:
	   - all work buffers allocated
	   - urbs allocated and ready to fill
	   - max rx packet known (in max_rx)
	   - unwrap state machine initialized, in state outside of any frame
	   - receive request in progress
	   - IrLAP layer started, about to hand over packets to send
	 */

	return 0;

      close_irlap:
	irlap_close(kingsun->irlap);
      free_mem:
	usb_free_urb(kingsun->speed_urb);
	kingsun->speed_urb = NULL;
	usb_free_urb(kingsun->tx_urb);
	kingsun->tx_urb = NULL;
	usb_free_urb(kingsun->rx_urb);
	kingsun->rx_urb = NULL;
	if (kingsun->rx_unwrap_buff.skb) {
		kfree_skb(kingsun->rx_unwrap_buff.skb);
		kingsun->rx_unwrap_buff.skb = NULL;
		kingsun->rx_unwrap_buff.head = NULL;
	}
	return err;
}

/*
 * Function ksdazzle_net_close (dev)
 *
 *    Network device is taken down. Usually this is done by
 *    "ifconfig irda0 down"
 */
static int ksdazzle_net_close(struct net_device *netdev)
{
	struct ksdazzle_cb *kingsun = netdev_priv(netdev);

	/* Stop transmit processing */
	netif_stop_queue(netdev);

	/* Mop up receive && transmit urb's */
	usb_kill_urb(kingsun->tx_urb);
	usb_free_urb(kingsun->tx_urb);
	kingsun->tx_urb = NULL;

	usb_kill_urb(kingsun->speed_urb);
	usb_free_urb(kingsun->speed_urb);
	kingsun->speed_urb = NULL;

	usb_kill_urb(kingsun->rx_urb);
	usb_free_urb(kingsun->rx_urb);
	kingsun->rx_urb = NULL;

	kfree_skb(kingsun->rx_unwrap_buff.skb);
	kingsun->rx_unwrap_buff.skb = NULL;
	kingsun->rx_unwrap_buff.head = NULL;
	kingsun->rx_unwrap_buff.in_frame = FALSE;
	kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME;
	kingsun->receiving = 0;

	/* Stop and remove instance of IrLAP */
	irlap_close(kingsun->irlap);

	kingsun->irlap = NULL;

	return 0;
}

/*
 * IOCTLs : Extra out-of-band network commands...
 */
static int ksdazzle_net_ioctl(struct net_device *netdev, struct ifreq *rq,
			      int cmd)
{
	struct if_irda_req *irq = (struct if_irda_req *)rq;
	struct ksdazzle_cb *kingsun = netdev_priv(netdev);
	int ret = 0;

	switch (cmd) {
	case SIOCSBANDWIDTH:	/* Set bandwidth */
		if (!capable(CAP_NET_ADMIN))
			return -EPERM;

		/* Check if the device is still there */
		if (netif_device_present(kingsun->netdev))
			return ksdazzle_change_speed(kingsun,
						     irq->ifr_baudrate);
		break;

	case SIOCSMEDIABUSY:	/* Set media busy */
		if (!capable(CAP_NET_ADMIN))
			return -EPERM;

		/* Check if the IrDA stack is still there */
		if (netif_running(kingsun->netdev))
			irda_device_set_media_busy(kingsun->netdev, TRUE);
		break;

	case SIOCGRECEIVING:
		/* Only approximately true */
		irq->ifr_receiving = kingsun->receiving;
		break;

	default:
		ret = -EOPNOTSUPP;
	}

	return ret;
}

static const struct net_device_ops ksdazzle_ops = {
	.ndo_start_xmit	= ksdazzle_hard_xmit,
	.ndo_open	= ksdazzle_net_open,
	.ndo_stop	= ksdazzle_net_close,
	.ndo_do_ioctl	= ksdazzle_net_ioctl,
};

/*
 * This routine is called by the USB subsystem for each new device
 * in the system. We need to check if the device is ours, and in
 * this case start handling it.
 */
static int ksdazzle_probe(struct usb_interface *intf,
			  const struct usb_device_id *id)
{
	struct usb_host_interface *interface;
	struct usb_endpoint_descriptor *endpoint;

	struct usb_device *dev = interface_to_usbdev(intf);
	struct ksdazzle_cb *kingsun = NULL;
	struct net_device *net = NULL;
	int ret = -ENOMEM;
	int pipe, maxp_in, maxp_out;
	__u8 ep_in;
	__u8 ep_out;

	/* Check that there really are two interrupt endpoints. Check based on the
	   one in drivers/usb/input/usbmouse.c
	 */
	interface = intf->cur_altsetting;
	if (interface->desc.bNumEndpoints != 2) {
		err("ksdazzle: expected 2 endpoints, found %d",
		    interface->desc.bNumEndpoints);
		return -ENODEV;
	}
	endpoint = &interface->endpoint[KINGSUN_EP_IN].desc;
	if (!usb_endpoint_is_int_in(endpoint)) {
		err("ksdazzle: endpoint 0 is not interrupt IN");
		return -ENODEV;
	}

	ep_in = endpoint->bEndpointAddress;
	pipe = usb_rcvintpipe(dev, ep_in);
	maxp_in = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
	if (maxp_in > 255 || maxp_in <= 1) {
		err("ksdazzle: endpoint 0 has max packet size %d not in range [2..255]", maxp_in);
		return -ENODEV;
	}

	endpoint = &interface->endpoint[KINGSUN_EP_OUT].desc;
	if (!usb_endpoint_is_int_out(endpoint)) {
		err("ksdazzle: endpoint 1 is not interrupt OUT");
		return -ENODEV;
	}

	ep_out = endpoint->bEndpointAddress;
	pipe = usb_sndintpipe(dev, ep_out);
	maxp_out = usb_maxpacket(dev, pipe, usb_pipeout(pipe));

	/* Allocate network device container. */
	net = alloc_irdadev(sizeof(*kingsun));
	if (!net)
		goto err_out1;

	SET_NETDEV_DEV(net, &intf->dev);
	kingsun = netdev_priv(net);
	kingsun->netdev = net;
	kingsun->usbdev = dev;
	kingsun->ep_in = ep_in;
	kingsun->ep_out = ep_out;
	kingsun->irlap = NULL;
	kingsun->tx_urb = NULL;
	kingsun->tx_buf_clear = NULL;
	kingsun->tx_buf_clear_used = 0;
	kingsun->tx_buf_clear_sent = 0;

	kingsun->rx_urb = NULL;
	kingsun->rx_buf = NULL;
	kingsun->rx_unwrap_buff.in_frame = FALSE;
	kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME;
	kingsun->rx_unwrap_buff.skb = NULL;
	kingsun->receiving = 0;
	spin_lock_init(&kingsun->lock);

	kingsun->speed_setuprequest = NULL;
	kingsun->speed_urb = NULL;
	kingsun->speedparams.baudrate = 0;

	/* Allocate input buffer */
	kingsun->rx_buf = kmalloc(KINGSUN_RCV_MAX, GFP_KERNEL);
	if (!kingsun->rx_buf)
		goto free_mem;

	/* Allocate output buffer */
	kingsun->tx_buf_clear = kmalloc(KINGSUN_SND_FIFO_SIZE, GFP_KERNEL);
	if (!kingsun->tx_buf_clear)
		goto free_mem;

	/* Allocate and initialize speed setup packet */
	kingsun->speed_setuprequest =
	    kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
	if (!kingsun->speed_setuprequest)
		goto free_mem;
	kingsun->speed_setuprequest->bRequestType =
	    USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
	kingsun->speed_setuprequest->bRequest = KINGSUN_REQ_SEND;
	kingsun->speed_setuprequest->wValue = cpu_to_le16(0x0200);
	kingsun->speed_setuprequest->wIndex = cpu_to_le16(0x0001);
	kingsun->speed_setuprequest->wLength =
	    cpu_to_le16(sizeof(struct ksdazzle_speedparams));

	printk(KERN_INFO "KingSun/Dazzle IRDA/USB found at address %d, "
	       "Vendor: %x, Product: %x\n",
	       dev->devnum, le16_to_cpu(dev->descriptor.idVendor),
	       le16_to_cpu(dev->descriptor.idProduct));

	/* Initialize QoS for this device */
	irda_init_max_qos_capabilies(&kingsun->qos);

	/* Baud rates known to be supported. Please uncomment if devices (other
	   than a SonyEriccson K300 phone) can be shown to support higher speeds
	   with this dongle.
	 */
	kingsun->qos.baud_rate.bits =
	    IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200;
	kingsun->qos.min_turn_time.bits &= KINGSUN_MTT;
	irda_qos_bits_to_value(&kingsun->qos);

	/* Override the network functions we need to use */
	net->netdev_ops = &ksdazzle_ops;

	ret = register_netdev(net);
	if (ret != 0)
		goto free_mem;

	dev_info(&net->dev, "IrDA: Registered KingSun/Dazzle device %s\n",
		 net->name);

	usb_set_intfdata(intf, kingsun);

	/* Situation at this point:
	   - all work buffers allocated
	   - setup requests pre-filled
	   - urbs not allocated, set to NULL
	   - max rx packet known (is KINGSUN_FIFO_SIZE)
	   - unwrap state machine (partially) initialized, but skb == NULL
	 */

	return 0;

      free_mem:
	kfree(kingsun->speed_setuprequest);
	kfree(kingsun->tx_buf_clear);
	kfree(kingsun->rx_buf);
	free_netdev(net);
      err_out1:
	return ret;
}

/*
 * The current device is removed, the USB layer tell us to shut it down...
 */
static void ksdazzle_disconnect(struct usb_interface *intf)
{
	struct ksdazzle_cb *kingsun = usb_get_intfdata(intf);

	if (!kingsun)
		return;

	unregister_netdev(kingsun->netdev);

	/* Mop up receive && transmit urb's */
	usb_kill_urb(kingsun->speed_urb);
	usb_free_urb(kingsun->speed_urb);
	kingsun->speed_urb = NULL;

	usb_kill_urb(kingsun->tx_urb);
	usb_free_urb(kingsun->tx_urb);
	kingsun->tx_urb = NULL;

	usb_kill_urb(kingsun->rx_urb);
	usb_free_urb(kingsun->rx_urb);
	kingsun->rx_urb = NULL;

	kfree(kingsun->speed_setuprequest);
	kfree(kingsun->tx_buf_clear);
	kfree(kingsun->rx_buf);
	free_netdev(kingsun->netdev);

	usb_set_intfdata(intf, NULL);
}

#ifdef CONFIG_PM
/* USB suspend, so power off the transmitter/receiver */
static int ksdazzle_suspend(struct usb_interface *intf, pm_message_t message)
{
	struct ksdazzle_cb *kingsun = usb_get_intfdata(intf);

	netif_device_detach(kingsun->netdev);
	if (kingsun->speed_urb != NULL)
		usb_kill_urb(kingsun->speed_urb);
	if (kingsun->tx_urb != NULL)
		usb_kill_urb(kingsun->tx_urb);
	if (kingsun->rx_urb != NULL)
		usb_kill_urb(kingsun->rx_urb);
	return 0;
}

/* Coming out of suspend, so reset hardware */
static int ksdazzle_resume(struct usb_interface *intf)
{
	struct ksdazzle_cb *kingsun = usb_get_intfdata(intf);

	if (kingsun->rx_urb != NULL) {
		/* Setup request already filled in ksdazzle_probe */
		usb_submit_urb(kingsun->rx_urb, GFP_KERNEL);
	}
	netif_device_attach(kingsun->netdev);

	return 0;
}
#endif

/*
 * USB device callbacks
 */
static struct usb_driver irda_driver = {
	.name = "ksdazzle-sir",
	.probe = ksdazzle_probe,
	.disconnect = ksdazzle_disconnect,
	.id_table = dongles,
#ifdef CONFIG_PM
	.suspend = ksdazzle_suspend,
	.resume = ksdazzle_resume,
#endif
};

/*
 * Module insertion
 */
static int __init ksdazzle_init(void)
{
	return usb_register(&irda_driver);
}

module_init(ksdazzle_init);

/*
 * Module removal
 */
static void __exit ksdazzle_cleanup(void)
{
	/* Deregister the driver and remove all pending instances */
	usb_deregister(&irda_driver);
}

module_exit(ksdazzle_cleanup);

MODULE_AUTHOR("Alex Villacís Lasso <a_villacis@palosanto.com>");
MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun Dazzle");
MODULE_LICENSE("GPL");
i_dev; struct net_device *ndev; #ifdef NS83820_VLAN_ACCEL_SUPPORT struct vlan_group *vlgrp; #endif struct rx_info rx_info; struct tasklet_struct rx_tasklet; unsigned ihr; struct work_struct tq_refill; /* protects everything below. irqsave when using. */ spinlock_t misc_lock; u32 CFG_cache; u32 MEAR_cache; u32 IMR_cache; unsigned linkstate; spinlock_t tx_lock; u16 tx_done_idx; u16 tx_idx; volatile u16 tx_free_idx; /* idx of free desc chain */ u16 tx_intr_idx; atomic_t nr_tx_skbs; struct sk_buff *tx_skbs[NR_TX_DESC]; char pad[16] __attribute__((aligned(16))); __le32 *tx_descs; dma_addr_t tx_phy_descs; struct timer_list tx_watchdog; }; static inline struct ns83820 *PRIV(struct net_device *dev) { return netdev_priv(dev); } #define __kick_rx(dev) writel(CR_RXE, dev->base + CR) static inline void kick_rx(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); dprintk("kick_rx: maybe kicking\n"); if (test_and_clear_bit(0, &dev->rx_info.idle)) { dprintk("actually kicking\n"); writel(dev->rx_info.phy_descs + (4 * DESC_SIZE * dev->rx_info.next_rx), dev->base + RXDP); if (dev->rx_info.next_rx == dev->rx_info.next_empty) printk(KERN_DEBUG "%s: uh-oh: next_rx == next_empty???\n", ndev->name); __kick_rx(dev); } } //free = (tx_done_idx + NR_TX_DESC-2 - free_idx) % NR_TX_DESC #define start_tx_okay(dev) \ (((NR_TX_DESC-2 + dev->tx_done_idx - dev->tx_free_idx) % NR_TX_DESC) > MIN_TX_DESC_FREE) #ifdef NS83820_VLAN_ACCEL_SUPPORT static void ns83820_vlan_rx_register(struct net_device *ndev, struct vlan_group *grp) { struct ns83820 *dev = PRIV(ndev); spin_lock_irq(&dev->misc_lock); spin_lock(&dev->tx_lock); dev->vlgrp = grp; spin_unlock(&dev->tx_lock); spin_unlock_irq(&dev->misc_lock); } #endif /* Packet Receiver * * The hardware supports linked lists of receive descriptors for * which ownership is transfered back and forth by means of an * ownership bit. While the hardware does support the use of a * ring for receive descriptors, we only make use of a chain in * an attempt to reduce bus traffic under heavy load scenarios. * This will also make bugs a bit more obvious. The current code * only makes use of a single rx chain; I hope to implement * priority based rx for version 1.0. Goal: even under overload * conditions, still route realtime traffic with as low jitter as * possible. */ static inline void build_rx_desc(struct ns83820 *dev, __le32 *desc, dma_addr_t link, dma_addr_t buf, u32 cmdsts, u32 extsts) { desc_addr_set(desc + DESC_LINK, link); desc_addr_set(desc + DESC_BUFPTR, buf); desc[DESC_EXTSTS] = cpu_to_le32(extsts); mb(); desc[DESC_CMDSTS] = cpu_to_le32(cmdsts); } #define nr_rx_empty(dev) ((NR_RX_DESC-2 + dev->rx_info.next_rx - dev->rx_info.next_empty) % NR_RX_DESC) static inline int ns83820_add_rx_skb(struct ns83820 *dev, struct sk_buff *skb) { unsigned next_empty; u32 cmdsts; __le32 *sg; dma_addr_t buf; next_empty = dev->rx_info.next_empty; /* don't overrun last rx marker */ if (unlikely(nr_rx_empty(dev) <= 2)) { kfree_skb(skb); return 1; } #if 0 dprintk("next_empty[%d] nr_used[%d] next_rx[%d]\n", dev->rx_info.next_empty, dev->rx_info.nr_used, dev->rx_info.next_rx ); #endif sg = dev->rx_info.descs + (next_empty * DESC_SIZE); BUG_ON(NULL != dev->rx_info.skbs[next_empty]); dev->rx_info.skbs[next_empty] = skb; dev->rx_info.next_empty = (next_empty + 1) % NR_RX_DESC; cmdsts = REAL_RX_BUF_SIZE | CMDSTS_INTR; buf = pci_map_single(dev->pci_dev, skb->data, REAL_RX_BUF_SIZE, PCI_DMA_FROMDEVICE); build_rx_desc(dev, sg, 0, buf, cmdsts, 0); /* update link of previous rx */ if (likely(next_empty != dev->rx_info.next_rx)) dev->rx_info.descs[((NR_RX_DESC + next_empty - 1) % NR_RX_DESC) * DESC_SIZE] = cpu_to_le32(dev->rx_info.phy_descs + (next_empty * DESC_SIZE * 4)); return 0; } static inline int rx_refill(struct net_device *ndev, gfp_t gfp) { struct ns83820 *dev = PRIV(ndev); unsigned i; unsigned long flags = 0; if (unlikely(nr_rx_empty(dev) <= 2)) return 0; dprintk("rx_refill(%p)\n", ndev); if (gfp == GFP_ATOMIC) spin_lock_irqsave(&dev->rx_info.lock, flags); for (i=0; i<NR_RX_DESC; i++) { struct sk_buff *skb; long res; /* extra 16 bytes for alignment */ skb = __netdev_alloc_skb(ndev, REAL_RX_BUF_SIZE+16, gfp); if (unlikely(!skb)) break; skb_reserve(skb, skb->data - PTR_ALIGN(skb->data, 16)); if (gfp != GFP_ATOMIC) spin_lock_irqsave(&dev->rx_info.lock, flags); res = ns83820_add_rx_skb(dev, skb); if (gfp != GFP_ATOMIC) spin_unlock_irqrestore(&dev->rx_info.lock, flags); if (res) { i = 1; break; } } if (gfp == GFP_ATOMIC) spin_unlock_irqrestore(&dev->rx_info.lock, flags); return i ? 0 : -ENOMEM; } static void rx_refill_atomic(struct net_device *ndev) { rx_refill(ndev, GFP_ATOMIC); } /* REFILL */ static inline void queue_refill(struct work_struct *work) { struct ns83820 *dev = container_of(work, struct ns83820, tq_refill); struct net_device *ndev = dev->ndev; rx_refill(ndev, GFP_KERNEL); if (dev->rx_info.up) kick_rx(ndev); } static inline void clear_rx_desc(struct ns83820 *dev, unsigned i) { build_rx_desc(dev, dev->rx_info.descs + (DESC_SIZE * i), 0, 0, CMDSTS_OWN, 0); } static void phy_intr(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); static const char *speeds[] = { "10", "100", "1000", "1000(?)", "1000F" }; u32 cfg, new_cfg; u32 tbisr, tanar, tanlpar; int speed, fullduplex, newlinkstate; cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY; if (dev->CFG_cache & CFG_TBI_EN) { /* we have an optical transceiver */ tbisr = readl(dev->base + TBISR); tanar = readl(dev->base + TANAR); tanlpar = readl(dev->base + TANLPAR); dprintk("phy_intr: tbisr=%08x, tanar=%08x, tanlpar=%08x\n", tbisr, tanar, tanlpar); if ( (fullduplex = (tanlpar & TANAR_FULL_DUP) && (tanar & TANAR_FULL_DUP)) ) { /* both of us are full duplex */ writel(readl(dev->base + TXCFG) | TXCFG_CSI | TXCFG_HBI | TXCFG_ATP, dev->base + TXCFG); writel(readl(dev->base + RXCFG) | RXCFG_RX_FD, dev->base + RXCFG); /* Light up full duplex LED */ writel(readl(dev->base + GPIOR) | GPIOR_GP1_OUT, dev->base + GPIOR); } else if(((tanlpar & TANAR_HALF_DUP) && (tanar & TANAR_HALF_DUP)) || ((tanlpar & TANAR_FULL_DUP) && (tanar & TANAR_HALF_DUP)) || ((tanlpar & TANAR_HALF_DUP) && (tanar & TANAR_FULL_DUP))) { /* one or both of us are half duplex */ writel((readl(dev->base + TXCFG) & ~(TXCFG_CSI | TXCFG_HBI)) | TXCFG_ATP, dev->base + TXCFG); writel(readl(dev->base + RXCFG) & ~RXCFG_RX_FD, dev->base + RXCFG); /* Turn off full duplex LED */ writel(readl(dev->base + GPIOR) & ~GPIOR_GP1_OUT, dev->base + GPIOR); } speed = 4; /* 1000F */ } else { /* we have a copper transceiver */ new_cfg = dev->CFG_cache & ~(CFG_SB | CFG_MODE_1000 | CFG_SPDSTS); if (cfg & CFG_SPDSTS1) new_cfg |= CFG_MODE_1000; else new_cfg &= ~CFG_MODE_1000; speed = ((cfg / CFG_SPDSTS0) & 3); fullduplex = (cfg & CFG_DUPSTS); if (fullduplex) { new_cfg |= CFG_SB; writel(readl(dev->base + TXCFG) | TXCFG_CSI | TXCFG_HBI, dev->base + TXCFG); writel(readl(dev->base + RXCFG) | RXCFG_RX_FD, dev->base + RXCFG); } else { writel(readl(dev->base + TXCFG) & ~(TXCFG_CSI | TXCFG_HBI), dev->base + TXCFG); writel(readl(dev->base + RXCFG) & ~(RXCFG_RX_FD), dev->base + RXCFG); } if ((cfg & CFG_LNKSTS) && ((new_cfg ^ dev->CFG_cache) != 0)) { writel(new_cfg, dev->base + CFG); dev->CFG_cache = new_cfg; } dev->CFG_cache &= ~CFG_SPDSTS; dev->CFG_cache |= cfg & CFG_SPDSTS; } newlinkstate = (cfg & CFG_LNKSTS) ? LINK_UP : LINK_DOWN; if (newlinkstate & LINK_UP && dev->linkstate != newlinkstate) { netif_start_queue(ndev); netif_wake_queue(ndev); printk(KERN_INFO "%s: link now %s mbps, %s duplex and up.\n", ndev->name, speeds[speed], fullduplex ? "full" : "half"); } else if (newlinkstate & LINK_DOWN && dev->linkstate != newlinkstate) { netif_stop_queue(ndev); printk(KERN_INFO "%s: link now down.\n", ndev->name); } dev->linkstate = newlinkstate; } static int ns83820_setup_rx(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); unsigned i; int ret; dprintk("ns83820_setup_rx(%p)\n", ndev); dev->rx_info.idle = 1; dev->rx_info.next_rx = 0; dev->rx_info.next_rx_desc = dev->rx_info.descs; dev->rx_info.next_empty = 0; for (i=0; i<NR_RX_DESC; i++) clear_rx_desc(dev, i); writel(0, dev->base + RXDP_HI); writel(dev->rx_info.phy_descs, dev->base + RXDP); ret = rx_refill(ndev, GFP_KERNEL); if (!ret) { dprintk("starting receiver\n"); /* prevent the interrupt handler from stomping on us */ spin_lock_irq(&dev->rx_info.lock); writel(0x0001, dev->base + CCSR); writel(0, dev->base + RFCR); writel(0x7fc00000, dev->base + RFCR); writel(0xffc00000, dev->base + RFCR); dev->rx_info.up = 1; phy_intr(ndev); /* Okay, let it rip */ spin_lock_irq(&dev->misc_lock); dev->IMR_cache |= ISR_PHY; dev->IMR_cache |= ISR_RXRCMP; //dev->IMR_cache |= ISR_RXERR; //dev->IMR_cache |= ISR_RXOK; dev->IMR_cache |= ISR_RXORN; dev->IMR_cache |= ISR_RXSOVR; dev->IMR_cache |= ISR_RXDESC; dev->IMR_cache |= ISR_RXIDLE; dev->IMR_cache |= ISR_TXDESC; dev->IMR_cache |= ISR_TXIDLE; writel(dev->IMR_cache, dev->base + IMR); writel(1, dev->base + IER); spin_unlock(&dev->misc_lock); kick_rx(ndev); spin_unlock_irq(&dev->rx_info.lock); } return ret; } static void ns83820_cleanup_rx(struct ns83820 *dev) { unsigned i; unsigned long flags; dprintk("ns83820_cleanup_rx(%p)\n", dev); /* disable receive interrupts */ spin_lock_irqsave(&dev->misc_lock, flags); dev->IMR_cache &= ~(ISR_RXOK | ISR_RXDESC | ISR_RXERR | ISR_RXEARLY | ISR_RXIDLE); writel(dev->IMR_cache, dev->base + IMR); spin_unlock_irqrestore(&dev->misc_lock, flags); /* synchronize with the interrupt handler and kill it */ dev->rx_info.up = 0; synchronize_irq(dev->pci_dev->irq); /* touch the pci bus... */ readl(dev->base + IMR); /* assumes the transmitter is already disabled and reset */ writel(0, dev->base + RXDP_HI); writel(0, dev->base + RXDP); for (i=0; i<NR_RX_DESC; i++) { struct sk_buff *skb = dev->rx_info.skbs[i]; dev->rx_info.skbs[i] = NULL; clear_rx_desc(dev, i); kfree_skb(skb); } } static void ns83820_rx_kick(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); /*if (nr_rx_empty(dev) >= NR_RX_DESC/4)*/ { if (dev->rx_info.up) { rx_refill_atomic(ndev); kick_rx(ndev); } } if (dev->rx_info.up && nr_rx_empty(dev) > NR_RX_DESC*3/4) schedule_work(&dev->tq_refill); else kick_rx(ndev); if (dev->rx_info.idle) printk(KERN_DEBUG "%s: BAD\n", ndev->name); } /* rx_irq * */ static void rx_irq(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); struct rx_info *info = &dev->rx_info; unsigned next_rx; int rx_rc, len; u32 cmdsts; __le32 *desc; unsigned long flags; int nr = 0; dprintk("rx_irq(%p)\n", ndev); dprintk("rxdp: %08x, descs: %08lx next_rx[%d]: %p next_empty[%d]: %p\n", readl(dev->base + RXDP), (long)(dev->rx_info.phy_descs), (int)dev->rx_info.next_rx, (dev->rx_info.descs + (DESC_SIZE * dev->rx_info.next_rx)), (int)dev->rx_info.next_empty, (dev->rx_info.descs + (DESC_SIZE * dev->rx_info.next_empty)) ); spin_lock_irqsave(&info->lock, flags); if (!info->up) goto out; dprintk("walking descs\n"); next_rx = info->next_rx; desc = info->next_rx_desc; while ((CMDSTS_OWN & (cmdsts = le32_to_cpu(desc[DESC_CMDSTS]))) && (cmdsts != CMDSTS_OWN)) { struct sk_buff *skb; u32 extsts = le32_to_cpu(desc[DESC_EXTSTS]); dma_addr_t bufptr = desc_addr_get(desc + DESC_BUFPTR); dprintk("cmdsts: %08x\n", cmdsts); dprintk("link: %08x\n", cpu_to_le32(desc[DESC_LINK])); dprintk("extsts: %08x\n", extsts); skb = info->skbs[next_rx]; info->skbs[next_rx] = NULL; info->next_rx = (next_rx + 1) % NR_RX_DESC; mb(); clear_rx_desc(dev, next_rx); pci_unmap_single(dev->pci_dev, bufptr, RX_BUF_SIZE, PCI_DMA_FROMDEVICE); len = cmdsts & CMDSTS_LEN_MASK; #ifdef NS83820_VLAN_ACCEL_SUPPORT /* NH: As was mentioned below, this chip is kinda * brain dead about vlan tag stripping. Frames * that are 64 bytes with a vlan header appended * like arp frames, or pings, are flagged as Runts * when the tag is stripped and hardware. This * also means that the OK bit in the descriptor * is cleared when the frame comes in so we have * to do a specific length check here to make sure * the frame would have been ok, had we not stripped * the tag. */ if (likely((CMDSTS_OK & cmdsts) || ((cmdsts & CMDSTS_RUNT) && len >= 56))) { #else if (likely(CMDSTS_OK & cmdsts)) { #endif skb_put(skb, len); if (unlikely(!skb)) goto netdev_mangle_me_harder_failed; if (cmdsts & CMDSTS_DEST_MULTI) dev->stats.multicast ++; dev->stats.rx_packets ++; dev->stats.rx_bytes += len; if ((extsts & 0x002a0000) && !(extsts & 0x00540000)) { skb->ip_summed = CHECKSUM_UNNECESSARY; } else { skb->ip_summed = CHECKSUM_NONE; } skb->protocol = eth_type_trans(skb, ndev); #ifdef NS83820_VLAN_ACCEL_SUPPORT if(extsts & EXTSTS_VPKT) { unsigned short tag; tag = ntohs(extsts & EXTSTS_VTG_MASK); rx_rc = vlan_hwaccel_rx(skb,dev->vlgrp,tag); } else { rx_rc = netif_rx(skb); } #else rx_rc = netif_rx(skb); #endif if (NET_RX_DROP == rx_rc) { netdev_mangle_me_harder_failed: dev->stats.rx_dropped ++; } } else { kfree_skb(skb); } nr++; next_rx = info->next_rx; desc = info->descs + (DESC_SIZE * next_rx); } info->next_rx = next_rx; info->next_rx_desc = info->descs + (DESC_SIZE * next_rx); out: if (0 && !nr) { Dprintk("dazed: cmdsts_f: %08x\n", cmdsts); } spin_unlock_irqrestore(&info->lock, flags); } static void rx_action(unsigned long _dev) { struct net_device *ndev = (void *)_dev; struct ns83820 *dev = PRIV(ndev); rx_irq(ndev); writel(ihr, dev->base + IHR); spin_lock_irq(&dev->misc_lock); dev->IMR_cache |= ISR_RXDESC; writel(dev->IMR_cache, dev->base + IMR); spin_unlock_irq(&dev->misc_lock); rx_irq(ndev); ns83820_rx_kick(ndev); } /* Packet Transmit code */ static inline void kick_tx(struct ns83820 *dev) { dprintk("kick_tx(%p): tx_idx=%d free_idx=%d\n", dev, dev->tx_idx, dev->tx_free_idx); writel(CR_TXE, dev->base + CR); } /* No spinlock needed on the transmit irq path as the interrupt handler is * serialized. */ static void do_tx_done(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); u32 cmdsts, tx_done_idx; __le32 *desc; dprintk("do_tx_done(%p)\n", ndev); tx_done_idx = dev->tx_done_idx; desc = dev->tx_descs + (tx_done_idx * DESC_SIZE); dprintk("tx_done_idx=%d free_idx=%d cmdsts=%08x\n", tx_done_idx, dev->tx_free_idx, le32_to_cpu(desc[DESC_CMDSTS])); while ((tx_done_idx != dev->tx_free_idx) && !(CMDSTS_OWN & (cmdsts = le32_to_cpu(desc[DESC_CMDSTS]))) ) { struct sk_buff *skb; unsigned len; dma_addr_t addr; if (cmdsts & CMDSTS_ERR) dev->stats.tx_errors ++; if (cmdsts & CMDSTS_OK) dev->stats.tx_packets ++; if (cmdsts & CMDSTS_OK) dev->stats.tx_bytes += cmdsts & 0xffff; dprintk("tx_done_idx=%d free_idx=%d cmdsts=%08x\n", tx_done_idx, dev->tx_free_idx, cmdsts); skb = dev->tx_skbs[tx_done_idx]; dev->tx_skbs[tx_done_idx] = NULL; dprintk("done(%p)\n", skb); len = cmdsts & CMDSTS_LEN_MASK; addr = desc_addr_get(desc + DESC_BUFPTR); if (skb) { pci_unmap_single(dev->pci_dev, addr, len, PCI_DMA_TODEVICE); dev_kfree_skb_irq(skb); atomic_dec(&dev->nr_tx_skbs); } else pci_unmap_page(dev->pci_dev, addr, len, PCI_DMA_TODEVICE); tx_done_idx = (tx_done_idx + 1) % NR_TX_DESC; dev->tx_done_idx = tx_done_idx; desc[DESC_CMDSTS] = cpu_to_le32(0); mb(); desc = dev->tx_descs + (tx_done_idx * DESC_SIZE); } /* Allow network stack to resume queueing packets after we've * finished transmitting at least 1/4 of the packets in the queue. */ if (netif_queue_stopped(ndev) && start_tx_okay(dev)) { dprintk("start_queue(%p)\n", ndev); netif_start_queue(ndev); netif_wake_queue(ndev); } } static void ns83820_cleanup_tx(struct ns83820 *dev) { unsigned i; for (i=0; i<NR_TX_DESC; i++) { struct sk_buff *skb = dev->tx_skbs[i]; dev->tx_skbs[i] = NULL; if (skb) { __le32 *desc = dev->tx_descs + (i * DESC_SIZE); pci_unmap_single(dev->pci_dev, desc_addr_get(desc + DESC_BUFPTR), le32_to_cpu(desc[DESC_CMDSTS]) & CMDSTS_LEN_MASK, PCI_DMA_TODEVICE); dev_kfree_skb_irq(skb); atomic_dec(&dev->nr_tx_skbs); } } memset(dev->tx_descs, 0, NR_TX_DESC * DESC_SIZE * 4); } /* transmit routine. This code relies on the network layer serializing * its calls in, but will run happily in parallel with the interrupt * handler. This code currently has provisions for fragmenting tx buffers * while trying to track down a bug in either the zero copy code or * the tx fifo (hence the MAX_FRAG_LEN). */ static int ns83820_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); u32 free_idx, cmdsts, extsts; int nr_free, nr_frags; unsigned tx_done_idx, last_idx; dma_addr_t buf; unsigned len; skb_frag_t *frag; int stopped = 0; int do_intr = 0; volatile __le32 *first_desc; dprintk("ns83820_hard_start_xmit\n"); nr_frags = skb_shinfo(skb)->nr_frags; again: if (unlikely(dev->CFG_cache & CFG_LNKSTS)) { netif_stop_queue(ndev); if (unlikely(dev->CFG_cache & CFG_LNKSTS)) return 1; netif_start_queue(ndev); } last_idx = free_idx = dev->tx_free_idx; tx_done_idx = dev->tx_done_idx; nr_free = (tx_done_idx + NR_TX_DESC-2 - free_idx) % NR_TX_DESC; nr_free -= 1; if (nr_free <= nr_frags) { dprintk("stop_queue - not enough(%p)\n", ndev); netif_stop_queue(ndev); /* Check again: we may have raced with a tx done irq */ if (dev->tx_done_idx != tx_done_idx) { dprintk("restart queue(%p)\n", ndev); netif_start_queue(ndev); goto again; } return 1; } if (free_idx == dev->tx_intr_idx) { do_intr = 1; dev->tx_intr_idx = (dev->tx_intr_idx + NR_TX_DESC/4) % NR_TX_DESC; } nr_free -= nr_frags; if (nr_free < MIN_TX_DESC_FREE) { dprintk("stop_queue - last entry(%p)\n", ndev); netif_stop_queue(ndev); stopped = 1; } frag = skb_shinfo(skb)->frags; if (!nr_frags) frag = NULL; extsts = 0; if (skb->ip_summed == CHECKSUM_PARTIAL) { extsts |= EXTSTS_IPPKT; if (IPPROTO_TCP == ip_hdr(skb)->protocol) extsts |= EXTSTS_TCPPKT; else if (IPPROTO_UDP == ip_hdr(skb)->protocol) extsts |= EXTSTS_UDPPKT; } #ifdef NS83820_VLAN_ACCEL_SUPPORT if(vlan_tx_tag_present(skb)) { /* fetch the vlan tag info out of the * ancilliary data if the vlan code * is using hw vlan acceleration */ short tag = vlan_tx_tag_get(skb); extsts |= (EXTSTS_VPKT | htons(tag)); } #endif len = skb->len; if (nr_frags) len -= skb->data_len; buf = pci_map_single(dev->pci_dev, skb->data, len, PCI_DMA_TODEVICE); first_desc = dev->tx_descs + (free_idx * DESC_SIZE); for (;;) { volatile __le32 *desc = dev->tx_descs + (free_idx * DESC_SIZE); dprintk("frag[%3u]: %4u @ 0x%08Lx\n", free_idx, len, (unsigned long long)buf); last_idx = free_idx; free_idx = (free_idx + 1) % NR_TX_DESC; desc[DESC_LINK] = cpu_to_le32(dev->tx_phy_descs + (free_idx * DESC_SIZE * 4)); desc_addr_set(desc + DESC_BUFPTR, buf); desc[DESC_EXTSTS] = cpu_to_le32(extsts); cmdsts = ((nr_frags) ? CMDSTS_MORE : do_intr ? CMDSTS_INTR : 0); cmdsts |= (desc == first_desc) ? 0 : CMDSTS_OWN; cmdsts |= len; desc[DESC_CMDSTS] = cpu_to_le32(cmdsts); if (!nr_frags) break; buf = pci_map_page(dev->pci_dev, frag->page, frag->page_offset, frag->size, PCI_DMA_TODEVICE); dprintk("frag: buf=%08Lx page=%08lx offset=%08lx\n", (long long)buf, (long) page_to_pfn(frag->page), frag->page_offset); len = frag->size; frag++; nr_frags--; } dprintk("done pkt\n"); spin_lock_irq(&dev->tx_lock); dev->tx_skbs[last_idx] = skb; first_desc[DESC_CMDSTS] |= cpu_to_le32(CMDSTS_OWN); dev->tx_free_idx = free_idx; atomic_inc(&dev->nr_tx_skbs); spin_unlock_irq(&dev->tx_lock); kick_tx(dev); /* Check again: we may have raced with a tx done irq */ if (stopped && (dev->tx_done_idx != tx_done_idx) && start_tx_okay(dev)) netif_start_queue(ndev); return NETDEV_TX_OK; } static void ns83820_update_stats(struct ns83820 *dev) { u8 __iomem *base = dev->base; /* the DP83820 will freeze counters, so we need to read all of them */ dev->stats.rx_errors += readl(base + 0x60) & 0xffff; dev->stats.rx_crc_errors += readl(base + 0x64) & 0xffff; dev->stats.rx_missed_errors += readl(base + 0x68) & 0xffff; dev->stats.rx_frame_errors += readl(base + 0x6c) & 0xffff; /*dev->stats.rx_symbol_errors +=*/ readl(base + 0x70); dev->stats.rx_length_errors += readl(base + 0x74) & 0xffff; dev->stats.rx_length_errors += readl(base + 0x78) & 0xffff; /*dev->stats.rx_badopcode_errors += */ readl(base + 0x7c); /*dev->stats.rx_pause_count += */ readl(base + 0x80); /*dev->stats.tx_pause_count += */ readl(base + 0x84); dev->stats.tx_carrier_errors += readl(base + 0x88) & 0xff; } static struct net_device_stats *ns83820_get_stats(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); /* somewhat overkill */ spin_lock_irq(&dev->misc_lock); ns83820_update_stats(dev); spin_unlock_irq(&dev->misc_lock); return &dev->stats; } /* Let ethtool retrieve info */ static int ns83820_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd) { struct ns83820 *dev = PRIV(ndev); u32 cfg, tanar, tbicr; int have_optical = 0; int fullduplex = 0; /* * Here's the list of available ethtool commands from other drivers: * cmd->advertising = * cmd->speed = * cmd->duplex = * cmd->port = 0; * cmd->phy_address = * cmd->transceiver = 0; * cmd->autoneg = * cmd->maxtxpkt = 0; * cmd->maxrxpkt = 0; */ /* read current configuration */ cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY; tanar = readl(dev->base + TANAR); tbicr = readl(dev->base + TBICR); if (dev->CFG_cache & CFG_TBI_EN) { /* we have an optical interface */ have_optical = 1; fullduplex = (cfg & CFG_DUPSTS) ? 1 : 0; } else { /* We have copper */ fullduplex = (cfg & CFG_DUPSTS) ? 1 : 0; } cmd->supported = SUPPORTED_Autoneg; /* we have optical interface */ if (dev->CFG_cache & CFG_TBI_EN) { cmd->supported |= SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE; cmd->port = PORT_FIBRE; } /* TODO: else copper related support */ cmd->duplex = fullduplex ? DUPLEX_FULL : DUPLEX_HALF; switch (cfg / CFG_SPDSTS0 & 3) { case 2: cmd->speed = SPEED_1000; break; case 1: cmd->speed = SPEED_100; break; default: cmd->speed = SPEED_10; break; } cmd->autoneg = (tbicr & TBICR_MR_AN_ENABLE) ? 1: 0; return 0; } /* Let ethool change settings*/ static int ns83820_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd) { struct ns83820 *dev = PRIV(ndev); u32 cfg, tanar; int have_optical = 0; int fullduplex = 0; /* read current configuration */ cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY; tanar = readl(dev->base + TANAR); if (dev->CFG_cache & CFG_TBI_EN) { /* we have optical */ have_optical = 1; fullduplex = (tanar & TANAR_FULL_DUP); } else { /* we have copper */ fullduplex = cfg & CFG_DUPSTS; } spin_lock_irq(&dev->misc_lock); spin_lock(&dev->tx_lock); /* Set duplex */ if (cmd->duplex != fullduplex) { if (have_optical) { /*set full duplex*/ if (cmd->duplex == DUPLEX_FULL) { /* force full duplex */ writel(readl(dev->base + TXCFG) | TXCFG_CSI | TXCFG_HBI | TXCFG_ATP, dev->base + TXCFG); writel(readl(dev->base + RXCFG) | RXCFG_RX_FD, dev->base + RXCFG); /* Light up full duplex LED */ writel(readl(dev->base + GPIOR) | GPIOR_GP1_OUT, dev->base + GPIOR); } else { /*TODO: set half duplex */ } } else { /*we have copper*/ /* TODO: Set duplex for copper cards */ } printk(KERN_INFO "%s: Duplex set via ethtool\n", ndev->name); } /* Set autonegotiation */ if (1) { if (cmd->autoneg == AUTONEG_ENABLE) { /* restart auto negotiation */ writel(TBICR_MR_AN_ENABLE | TBICR_MR_RESTART_AN, dev->base + TBICR); writel(TBICR_MR_AN_ENABLE, dev->base + TBICR); dev->linkstate = LINK_AUTONEGOTIATE; printk(KERN_INFO "%s: autoneg enabled via ethtool\n", ndev->name); } else { /* disable auto negotiation */ writel(0x00000000, dev->base + TBICR); } printk(KERN_INFO "%s: autoneg %s via ethtool\n", ndev->name, cmd->autoneg ? "ENABLED" : "DISABLED"); } phy_intr(ndev); spin_unlock(&dev->tx_lock); spin_unlock_irq(&dev->misc_lock); return 0; } /* end ethtool get/set support -df */ static void ns83820_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { struct ns83820 *dev = PRIV(ndev); strcpy(info->driver, "ns83820"); strcpy(info->version, VERSION); strcpy(info->bus_info, pci_name(dev->pci_dev)); } static u32 ns83820_get_link(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); u32 cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY; return cfg & CFG_LNKSTS ? 1 : 0; } static const struct ethtool_ops ops = { .get_settings = ns83820_get_settings, .set_settings = ns83820_set_settings, .get_drvinfo = ns83820_get_drvinfo, .get_link = ns83820_get_link }; /* this function is called in irq context from the ISR */ static void ns83820_mib_isr(struct ns83820 *dev) { unsigned long flags; spin_lock_irqsave(&dev->misc_lock, flags); ns83820_update_stats(dev); spin_unlock_irqrestore(&dev->misc_lock, flags); } static void ns83820_do_isr(struct net_device *ndev, u32 isr); static irqreturn_t ns83820_irq(int foo, void *data) { struct net_device *ndev = data; struct ns83820 *dev = PRIV(ndev); u32 isr; dprintk("ns83820_irq(%p)\n", ndev); dev->ihr = 0; isr = readl(dev->base + ISR); dprintk("irq: %08x\n", isr); ns83820_do_isr(ndev, isr); return IRQ_HANDLED; } static void ns83820_do_isr(struct net_device *ndev, u32 isr) { struct ns83820 *dev = PRIV(ndev); unsigned long flags; #ifdef DEBUG if (isr & ~(ISR_PHY | ISR_RXDESC | ISR_RXEARLY | ISR_RXOK | ISR_RXERR | ISR_TXIDLE | ISR_TXOK | ISR_TXDESC)) Dprintk("odd isr? 0x%08x\n", isr); #endif if (ISR_RXIDLE & isr) { dev->rx_info.idle = 1; Dprintk("oh dear, we are idle\n"); ns83820_rx_kick(ndev); } if ((ISR_RXDESC | ISR_RXOK) & isr) { prefetch(dev->rx_info.next_rx_desc); spin_lock_irqsave(&dev->misc_lock, flags); dev->IMR_cache &= ~(ISR_RXDESC | ISR_RXOK); writel(dev->IMR_cache, dev->base + IMR); spin_unlock_irqrestore(&dev->misc_lock, flags); tasklet_schedule(&dev->rx_tasklet); //rx_irq(ndev); //writel(4, dev->base + IHR); } if ((ISR_RXIDLE | ISR_RXORN | ISR_RXDESC | ISR_RXOK | ISR_RXERR) & isr) ns83820_rx_kick(ndev); if (unlikely(ISR_RXSOVR & isr)) { //printk("overrun: rxsovr\n"); dev->stats.rx_fifo_errors ++; } if (unlikely(ISR_RXORN & isr)) { //printk("overrun: rxorn\n"); dev->stats.rx_fifo_errors ++; } if ((ISR_RXRCMP & isr) && dev->rx_info.up) writel(CR_RXE, dev->base + CR); if (ISR_TXIDLE & isr) { u32 txdp; txdp = readl(dev->base + TXDP); dprintk("txdp: %08x\n", txdp); txdp -= dev->tx_phy_descs; dev->tx_idx = txdp / (DESC_SIZE * 4); if (dev->tx_idx >= NR_TX_DESC) { printk(KERN_ALERT "%s: BUG -- txdp out of range\n", ndev->name); dev->tx_idx = 0; } /* The may have been a race between a pci originated read * and the descriptor update from the cpu. Just in case, * kick the transmitter if the hardware thinks it is on a * different descriptor than we are. */ if (dev->tx_idx != dev->tx_free_idx) kick_tx(dev); } /* Defer tx ring processing until more than a minimum amount of * work has accumulated */ if ((ISR_TXDESC | ISR_TXIDLE | ISR_TXOK | ISR_TXERR) & isr) { spin_lock_irqsave(&dev->tx_lock, flags); do_tx_done(ndev); spin_unlock_irqrestore(&dev->tx_lock, flags); /* Disable TxOk if there are no outstanding tx packets. */ if ((dev->tx_done_idx == dev->tx_free_idx) && (dev->IMR_cache & ISR_TXOK)) { spin_lock_irqsave(&dev->misc_lock, flags); dev->IMR_cache &= ~ISR_TXOK; writel(dev->IMR_cache, dev->base + IMR); spin_unlock_irqrestore(&dev->misc_lock, flags); } } /* The TxIdle interrupt can come in before the transmit has * completed. Normally we reap packets off of the combination * of TxDesc and TxIdle and leave TxOk disabled (since it * occurs on every packet), but when no further irqs of this * nature are expected, we must enable TxOk. */ if ((ISR_TXIDLE & isr) && (dev->tx_done_idx != dev->tx_free_idx)) { spin_lock_irqsave(&dev->misc_lock, flags); dev->IMR_cache |= ISR_TXOK; writel(dev->IMR_cache, dev->base + IMR); spin_unlock_irqrestore(&dev->misc_lock, flags); } /* MIB interrupt: one of the statistics counters is about to overflow */ if (unlikely(ISR_MIB & isr)) ns83820_mib_isr(dev); /* PHY: Link up/down/negotiation state change */ if (unlikely(ISR_PHY & isr)) phy_intr(ndev); #if 0 /* Still working on the interrupt mitigation strategy */ if (dev->ihr) writel(dev->ihr, dev->base + IHR); #endif } static void ns83820_do_reset(struct ns83820 *dev, u32 which) { Dprintk("resetting chip...\n"); writel(which, dev->base + CR); do { schedule(); } while (readl(dev->base + CR) & which); Dprintk("okay!\n"); } static int ns83820_stop(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); /* FIXME: protect against interrupt handler? */ del_timer_sync(&dev->tx_watchdog); /* disable interrupts */ writel(0, dev->base + IMR); writel(0, dev->base + IER); readl(dev->base + IER); dev->rx_info.up = 0; synchronize_irq(dev->pci_dev->irq); ns83820_do_reset(dev, CR_RST); synchronize_irq(dev->pci_dev->irq); spin_lock_irq(&dev->misc_lock); dev->IMR_cache &= ~(ISR_TXURN | ISR_TXIDLE | ISR_TXERR | ISR_TXDESC | ISR_TXOK); spin_unlock_irq(&dev->misc_lock); ns83820_cleanup_rx(dev); ns83820_cleanup_tx(dev); return 0; } static void ns83820_tx_timeout(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); u32 tx_done_idx; __le32 *desc; unsigned long flags; spin_lock_irqsave(&dev->tx_lock, flags); tx_done_idx = dev->tx_done_idx; desc = dev->tx_descs + (tx_done_idx * DESC_SIZE); printk(KERN_INFO "%s: tx_timeout: tx_done_idx=%d free_idx=%d cmdsts=%08x\n", ndev->name, tx_done_idx, dev->tx_free_idx, le32_to_cpu(desc[DESC_CMDSTS])); #if defined(DEBUG) { u32 isr; isr = readl(dev->base + ISR); printk("irq: %08x imr: %08x\n", isr, dev->IMR_cache); ns83820_do_isr(ndev, isr); } #endif do_tx_done(ndev); tx_done_idx = dev->tx_done_idx; desc = dev->tx_descs + (tx_done_idx * DESC_SIZE); printk(KERN_INFO "%s: after: tx_done_idx=%d free_idx=%d cmdsts=%08x\n", ndev->name, tx_done_idx, dev->tx_free_idx, le32_to_cpu(desc[DESC_CMDSTS])); spin_unlock_irqrestore(&dev->tx_lock, flags); } static void ns83820_tx_watch(unsigned long data) { struct net_device *ndev = (void *)data; struct ns83820 *dev = PRIV(ndev); #if defined(DEBUG) printk("ns83820_tx_watch: %u %u %d\n", dev->tx_done_idx, dev->tx_free_idx, atomic_read(&dev->nr_tx_skbs) ); #endif if (time_after(jiffies, dev_trans_start(ndev) + 1*HZ) && dev->tx_done_idx != dev->tx_free_idx) { printk(KERN_DEBUG "%s: ns83820_tx_watch: %u %u %d\n", ndev->name, dev->tx_done_idx, dev->tx_free_idx, atomic_read(&dev->nr_tx_skbs)); ns83820_tx_timeout(ndev); } mod_timer(&dev->tx_watchdog, jiffies + 2*HZ); } static int ns83820_open(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); unsigned i; u32 desc; int ret; dprintk("ns83820_open\n"); writel(0, dev->base + PQCR); ret = ns83820_setup_rx(ndev); if (ret) goto failed; memset(dev->tx_descs, 0, 4 * NR_TX_DESC * DESC_SIZE); for (i=0; i<NR_TX_DESC; i++) { dev->tx_descs[(i * DESC_SIZE) + DESC_LINK] = cpu_to_le32( dev->tx_phy_descs + ((i+1) % NR_TX_DESC) * DESC_SIZE * 4); } dev->tx_idx = 0; dev->tx_done_idx = 0; desc = dev->tx_phy_descs; writel(0, dev->base + TXDP_HI); writel(desc, dev->base + TXDP); init_timer(&dev->tx_watchdog); dev->tx_watchdog.data = (unsigned long)ndev; dev->tx_watchdog.function = ns83820_tx_watch; mod_timer(&dev->tx_watchdog, jiffies + 2*HZ); netif_start_queue(ndev); /* FIXME: wait for phy to come up */ return 0; failed: ns83820_stop(ndev); return ret; } static void ns83820_getmac(struct ns83820 *dev, u8 *mac) { unsigned i; for (i=0; i<3; i++) { u32 data; /* Read from the perfect match memory: this is loaded by * the chip from the EEPROM via the EELOAD self test. */ writel(i*2, dev->base + RFCR); data = readl(dev->base + RFDR); *mac++ = data; *mac++ = data >> 8; } } static int ns83820_change_mtu(struct net_device *ndev, int new_mtu) { if (new_mtu > RX_BUF_SIZE) return -EINVAL; ndev->mtu = new_mtu; return 0; } static void ns83820_set_multicast(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); u8 __iomem *rfcr = dev->base + RFCR; u32 and_mask = 0xffffffff; u32 or_mask = 0; u32 val; if (ndev->flags & IFF_PROMISC) or_mask |= RFCR_AAU | RFCR_AAM; else and_mask &= ~(RFCR_AAU | RFCR_AAM); if (ndev->flags & IFF_ALLMULTI || ndev->mc_count) or_mask |= RFCR_AAM; else and_mask &= ~RFCR_AAM; spin_lock_irq(&dev->misc_lock); val = (readl(rfcr) & and_mask) | or_mask; /* Ramit : RFCR Write Fix doc says RFEN must be 0 modify other bits */ writel(val & ~RFCR_RFEN, rfcr); writel(val, rfcr); spin_unlock_irq(&dev->misc_lock); } static void ns83820_run_bist(struct net_device *ndev, const char *name, u32 enable, u32 done, u32 fail) { struct ns83820 *dev = PRIV(ndev); int timed_out = 0; unsigned long start; u32 status; int loops = 0; dprintk("%s: start %s\n", ndev->name, name); start = jiffies; writel(enable, dev->base + PTSCR); for (;;) { loops++; status = readl(dev->base + PTSCR); if (!(status & enable)) break; if (status & done) break; if (status & fail) break; if (time_after_eq(jiffies, start + HZ)) { timed_out = 1; break; } schedule_timeout_uninterruptible(1); } if (status & fail) printk(KERN_INFO "%s: %s failed! (0x%08x & 0x%08x)\n", ndev->name, name, status, fail); else if (timed_out) printk(KERN_INFO "%s: run_bist %s timed out! (%08x)\n", ndev->name, name, status); dprintk("%s: done %s in %d loops\n", ndev->name, name, loops); } #ifdef PHY_CODE_IS_FINISHED static void ns83820_mii_write_bit(struct ns83820 *dev, int bit) { /* drive MDC low */ dev->MEAR_cache &= ~MEAR_MDC; writel(dev->MEAR_cache, dev->base + MEAR); readl(dev->base + MEAR); /* enable output, set bit */ dev->MEAR_cache |= MEAR_MDDIR; if (bit) dev->MEAR_cache |= MEAR_MDIO; else dev->MEAR_cache &= ~MEAR_MDIO; /* set the output bit */ writel(dev->MEAR_cache, dev->base + MEAR); readl(dev->base + MEAR); /* Wait. Max clock rate is 2.5MHz, this way we come in under 1MHz */ udelay(1); /* drive MDC high causing the data bit to be latched */ dev->MEAR_cache |= MEAR_MDC; writel(dev->MEAR_cache, dev->base + MEAR); readl(dev->base + MEAR); /* Wait again... */ udelay(1); } static int ns83820_mii_read_bit(struct ns83820 *dev) { int bit; /* drive MDC low, disable output */ dev->MEAR_cache &= ~MEAR_MDC; dev->MEAR_cache &= ~MEAR_MDDIR; writel(dev->MEAR_cache, dev->base + MEAR); readl(dev->base + MEAR); /* Wait. Max clock rate is 2.5MHz, this way we come in under 1MHz */ udelay(1); /* drive MDC high causing the data bit to be latched */ bit = (readl(dev->base + MEAR) & MEAR_MDIO) ? 1 : 0; dev->MEAR_cache |= MEAR_MDC; writel(dev->MEAR_cache, dev->base + MEAR); /* Wait again... */ udelay(1); return bit; } static unsigned ns83820_mii_read_reg(struct ns83820 *dev, unsigned phy, unsigned reg) { unsigned data = 0; int i; /* read some garbage so that we eventually sync up */ for (i=0; i<64; i++) ns83820_mii_read_bit(dev); ns83820_mii_write_bit(dev, 0); /* start */ ns83820_mii_write_bit(dev, 1); ns83820_mii_write_bit(dev, 1); /* opcode read */ ns83820_mii_write_bit(dev, 0); /* write out the phy address: 5 bits, msb first */ for (i=0; i<5; i++) ns83820_mii_write_bit(dev, phy & (0x10 >> i)); /* write out the register address, 5 bits, msb first */ for (i=0; i<5; i++) ns83820_mii_write_bit(dev, reg & (0x10 >> i)); ns83820_mii_read_bit(dev); /* turn around cycles */ ns83820_mii_read_bit(dev); /* read in the register data, 16 bits msb first */ for (i=0; i<16; i++) { data <<= 1; data |= ns83820_mii_read_bit(dev); } return data; } static unsigned ns83820_mii_write_reg(struct ns83820 *dev, unsigned phy, unsigned reg, unsigned data) { int i; /* read some garbage so that we eventually sync up */ for (i=0; i<64; i++) ns83820_mii_read_bit(dev); ns83820_mii_write_bit(dev, 0); /* start */ ns83820_mii_write_bit(dev, 1); ns83820_mii_write_bit(dev, 0); /* opcode read */ ns83820_mii_write_bit(dev, 1); /* write out the phy address: 5 bits, msb first */ for (i=0; i<5; i++) ns83820_mii_write_bit(dev, phy & (0x10 >> i)); /* write out the register address, 5 bits, msb first */ for (i=0; i<5; i++) ns83820_mii_write_bit(dev, reg & (0x10 >> i)); ns83820_mii_read_bit(dev); /* turn around cycles */ ns83820_mii_read_bit(dev); /* read in the register data, 16 bits msb first */ for (i=0; i<16; i++) ns83820_mii_write_bit(dev, (data >> (15 - i)) & 1); return data; } static void ns83820_probe_phy(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); static int first; int i; #define MII_PHYIDR1 0x02 #define MII_PHYIDR2 0x03 #if 0 if (!first) { unsigned tmp; ns83820_mii_read_reg(dev, 1, 0x09); ns83820_mii_write_reg(dev, 1, 0x10, 0x0d3e); tmp = ns83820_mii_read_reg(dev, 1, 0x00); ns83820_mii_write_reg(dev, 1, 0x00, tmp | 0x8000); udelay(1300); ns83820_mii_read_reg(dev, 1, 0x09); } #endif first = 1; for (i=1; i<2; i++) { int j; unsigned a, b; a = ns83820_mii_read_reg(dev, i, MII_PHYIDR1); b = ns83820_mii_read_reg(dev, i, MII_PHYIDR2); //printk("%s: phy %d: 0x%04x 0x%04x\n", // ndev->name, i, a, b); for (j=0; j<0x16; j+=4) { dprintk("%s: [0x%02x] %04x %04x %04x %04x\n", ndev->name, j, ns83820_mii_read_reg(dev, i, 0 + j), ns83820_mii_read_reg(dev, i, 1 + j), ns83820_mii_read_reg(dev, i, 2 + j), ns83820_mii_read_reg(dev, i, 3 + j) ); } } { unsigned a, b; /* read firmware version: memory addr is 0x8402 and 0x8403 */ ns83820_mii_write_reg(dev, 1, 0x16, 0x000d); ns83820_mii_write_reg(dev, 1, 0x1e, 0x810e); a = ns83820_mii_read_reg(dev, 1, 0x1d); ns83820_mii_write_reg(dev, 1, 0x16, 0x000d); ns83820_mii_write_reg(dev, 1, 0x1e, 0x810e); b = ns83820_mii_read_reg(dev, 1, 0x1d); dprintk("version: 0x%04x 0x%04x\n", a, b); } } #endif static const struct net_device_ops netdev_ops = { .ndo_open = ns83820_open, .ndo_stop = ns83820_stop, .ndo_start_xmit = ns83820_hard_start_xmit, .ndo_get_stats = ns83820_get_stats, .ndo_change_mtu = ns83820_change_mtu, .ndo_set_multicast_list = ns83820_set_multicast, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, .ndo_tx_timeout = ns83820_tx_timeout, #ifdef NS83820_VLAN_ACCEL_SUPPORT .ndo_vlan_rx_register = ns83820_vlan_rx_register, #endif }; static int __devinit ns83820_init_one(struct pci_dev *pci_dev, const struct pci_device_id *id) { struct net_device *ndev; struct ns83820 *dev; long addr; int err; int using_dac = 0; /* See if we can set the dma mask early on; failure is fatal. */ if (sizeof(dma_addr_t) == 8 && !pci_set_dma_mask(pci_dev, DMA_BIT_MASK(64))) { using_dac = 1; } else if (!pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) { using_dac = 0; } else { dev_warn(&pci_dev->dev, "pci_set_dma_mask failed!\n"); return -ENODEV; } ndev = alloc_etherdev(sizeof(struct ns83820)); dev = PRIV(ndev); err = -ENOMEM; if (!dev) goto out; dev->ndev = ndev; spin_lock_init(&dev->rx_info.lock); spin_lock_init(&dev->tx_lock); spin_lock_init(&dev->misc_lock); dev->pci_dev = pci_dev; SET_NETDEV_DEV(ndev, &pci_dev->dev); INIT_WORK(&dev->tq_refill, queue_refill); tasklet_init(&dev->rx_tasklet, rx_action, (unsigned long)ndev); err = pci_enable_device(pci_dev); if (err) { dev_info(&pci_dev->dev, "pci_enable_dev failed: %d\n", err); goto out_free; } pci_set_master(pci_dev); addr = pci_resource_start(pci_dev, 1); dev->base = ioremap_nocache(addr, PAGE_SIZE); dev->tx_descs = pci_alloc_consistent(pci_dev, 4 * DESC_SIZE * NR_TX_DESC, &dev->tx_phy_descs); dev->rx_info.descs = pci_alloc_consistent(pci_dev, 4 * DESC_SIZE * NR_RX_DESC, &dev->rx_info.phy_descs); err = -ENOMEM; if (!dev->base || !dev->tx_descs || !dev->rx_info.descs) goto out_disable; dprintk("%p: %08lx %p: %08lx\n", dev->tx_descs, (long)dev->tx_phy_descs, dev->rx_info.descs, (long)dev->rx_info.phy_descs); /* disable interrupts */ writel(0, dev->base + IMR); writel(0, dev->base + IER); readl(dev->base + IER); dev->IMR_cache = 0; err = request_irq(pci_dev->irq, ns83820_irq, IRQF_SHARED, DRV_NAME, ndev); if (err) { dev_info(&pci_dev->dev, "unable to register irq %d, err %d\n", pci_dev->irq, err); goto out_disable; } /* * FIXME: we are holding rtnl_lock() over obscenely long area only * because some of the setup code uses dev->name. It's Wrong(tm) - * we should be using driver-specific names for all that stuff. * For now that will do, but we really need to come back and kill * most of the dev_alloc_name() users later. */ rtnl_lock(); err = dev_alloc_name(ndev, ndev->name); if (err < 0) { dev_info(&pci_dev->dev, "unable to get netdev name: %d\n", err); goto out_free_irq; } printk("%s: ns83820.c: 0x22c: %08x, subsystem: %04x:%04x\n", ndev->name, le32_to_cpu(readl(dev->base + 0x22c)), pci_dev->subsystem_vendor, pci_dev->subsystem_device); ndev->netdev_ops = &netdev_ops; SET_ETHTOOL_OPS(ndev, &ops); ndev->watchdog_timeo = 5 * HZ; pci_set_drvdata(pci_dev, ndev); ns83820_do_reset(dev, CR_RST); /* Must reset the ram bist before running it */ writel(PTSCR_RBIST_RST, dev->base + PTSCR); ns83820_run_bist(ndev, "sram bist", PTSCR_RBIST_EN, PTSCR_RBIST_DONE, PTSCR_RBIST_FAIL); ns83820_run_bist(ndev, "eeprom bist", PTSCR_EEBIST_EN, 0, PTSCR_EEBIST_FAIL); ns83820_run_bist(ndev, "eeprom load", PTSCR_EELOAD_EN, 0, 0); /* I love config registers */ dev->CFG_cache = readl(dev->base + CFG); if ((dev->CFG_cache & CFG_PCI64_DET)) { printk(KERN_INFO "%s: detected 64 bit PCI data bus.\n", ndev->name); /*dev->CFG_cache |= CFG_DATA64_EN;*/ if (!(dev->CFG_cache & CFG_DATA64_EN)) printk(KERN_INFO "%s: EEPROM did not enable 64 bit bus. Disabled.\n", ndev->name); } else dev->CFG_cache &= ~(CFG_DATA64_EN); dev->CFG_cache &= (CFG_TBI_EN | CFG_MRM_DIS | CFG_MWI_DIS | CFG_T64ADDR | CFG_DATA64_EN | CFG_EXT_125 | CFG_M64ADDR); dev->CFG_cache |= CFG_PINT_DUPSTS | CFG_PINT_LNKSTS | CFG_PINT_SPDSTS | CFG_EXTSTS_EN | CFG_EXD | CFG_PESEL; dev->CFG_cache |= CFG_REQALG; dev->CFG_cache |= CFG_POW; dev->CFG_cache |= CFG_TMRTEST; /* When compiled with 64 bit addressing, we must always enable * the 64 bit descriptor format. */ if (sizeof(dma_addr_t) == 8) dev->CFG_cache |= CFG_M64ADDR; if (using_dac) dev->CFG_cache |= CFG_T64ADDR; /* Big endian mode does not seem to do what the docs suggest */ dev->CFG_cache &= ~CFG_BEM; /* setup optical transceiver if we have one */ if (dev->CFG_cache & CFG_TBI_EN) { printk(KERN_INFO "%s: enabling optical transceiver\n", ndev->name); writel(readl(dev->base + GPIOR) | 0x3e8, dev->base + GPIOR); /* setup auto negotiation feature advertisement */ writel(readl(dev->base + TANAR) | TANAR_HALF_DUP | TANAR_FULL_DUP, dev->base + TANAR); /* start auto negotiation */ writel(TBICR_MR_AN_ENABLE | TBICR_MR_RESTART_AN, dev->base + TBICR); writel(TBICR_MR_AN_ENABLE, dev->base + TBICR); dev->linkstate = LINK_AUTONEGOTIATE; dev->CFG_cache |= CFG_MODE_1000; } writel(dev->CFG_cache, dev->base + CFG); dprintk("CFG: %08x\n", dev->CFG_cache); if (reset_phy) { printk(KERN_INFO "%s: resetting phy\n", ndev->name); writel(dev->CFG_cache | CFG_PHY_RST, dev->base + CFG); msleep(10); writel(dev->CFG_cache, dev->base + CFG); } #if 0 /* Huh? This sets the PCI latency register. Should be done via * the PCI layer. FIXME. */ if (readl(dev->base + SRR)) writel(readl(dev->base+0x20c) | 0xfe00, dev->base + 0x20c); #endif /* Note! The DMA burst size interacts with packet * transmission, such that the largest packet that * can be transmitted is 8192 - FLTH - burst size. * If only the transmit fifo was larger... */ /* Ramit : 1024 DMA is not a good idea, it ends up banging * some DELL and COMPAQ SMP systems */ writel(TXCFG_CSI | TXCFG_HBI | TXCFG_ATP | TXCFG_MXDMA512 | ((1600 / 32) * 0x100), dev->base + TXCFG); /* Flush the interrupt holdoff timer */ writel(0x000, dev->base + IHR); writel(0x100, dev->base + IHR); writel(0x000, dev->base + IHR); /* Set Rx to full duplex, don't accept runt, errored, long or length * range errored packets. Use 512 byte DMA. */ /* Ramit : 1024 DMA is not a good idea, it ends up banging * some DELL and COMPAQ SMP systems * Turn on ALP, only we are accpeting Jumbo Packets */ writel(RXCFG_AEP | RXCFG_ARP | RXCFG_AIRL | RXCFG_RX_FD | RXCFG_STRIPCRC //| RXCFG_ALP | (RXCFG_MXDMA512) | 0, dev->base + RXCFG); /* Disable priority queueing */ writel(0, dev->base + PQCR); /* Enable IP checksum validation and detetion of VLAN headers. * Note: do not set the reject options as at least the 0x102 * revision of the chip does not properly accept IP fragments * at least for UDP. */ /* Ramit : Be sure to turn on RXCFG_ARP if VLAN's are enabled, since * the MAC it calculates the packetsize AFTER stripping the VLAN * header, and if a VLAN Tagged packet of 64 bytes is received (like * a ping with a VLAN header) then the card, strips the 4 byte VLAN * tag and then checks the packet size, so if RXCFG_ARP is not enabled, * it discrards it!. These guys...... * also turn on tag stripping if hardware acceleration is enabled */ #ifdef NS83820_VLAN_ACCEL_SUPPORT #define VRCR_INIT_VALUE (VRCR_IPEN|VRCR_VTDEN|VRCR_VTREN) #else #define VRCR_INIT_VALUE (VRCR_IPEN|VRCR_VTDEN) #endif writel(VRCR_INIT_VALUE, dev->base + VRCR); /* Enable per-packet TCP/UDP/IP checksumming * and per packet vlan tag insertion if * vlan hardware acceleration is enabled */ #ifdef NS83820_VLAN_ACCEL_SUPPORT #define VTCR_INIT_VALUE (VTCR_PPCHK|VTCR_VPPTI) #else #define VTCR_INIT_VALUE VTCR_PPCHK #endif writel(VTCR_INIT_VALUE, dev->base + VTCR); /* Ramit : Enable async and sync pause frames */ /* writel(0, dev->base + PCR); */ writel((PCR_PS_MCAST | PCR_PS_DA | PCR_PSEN | PCR_FFLO_4K | PCR_FFHI_8K | PCR_STLO_4 | PCR_STHI_8 | PCR_PAUSE_CNT), dev->base + PCR); /* Disable Wake On Lan */ writel(0, dev->base + WCSR); ns83820_getmac(dev, ndev->dev_addr); /* Yes, we support dumb IP checksum on transmit */ ndev->features |= NETIF_F_SG; ndev->features |= NETIF_F_IP_CSUM; #ifdef NS83820_VLAN_ACCEL_SUPPORT /* We also support hardware vlan acceleration */ ndev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; #endif if (using_dac) { printk(KERN_INFO "%s: using 64 bit addressing.\n", ndev->name); ndev->features |= NETIF_F_HIGHDMA; } printk(KERN_INFO "%s: ns83820 v" VERSION ": DP83820 v%u.%u: %pM io=0x%08lx irq=%d f=%s\n", ndev->name, (unsigned)readl(dev->base + SRR) >> 8, (unsigned)readl(dev->base + SRR) & 0xff, ndev->dev_addr, addr, pci_dev->irq, (ndev->features & NETIF_F_HIGHDMA) ? "h,sg" : "sg" ); #ifdef PHY_CODE_IS_FINISHED ns83820_probe_phy(ndev); #endif err = register_netdevice(ndev); if (err) { printk(KERN_INFO "ns83820: unable to register netdev: %d\n", err); goto out_cleanup; } rtnl_unlock(); return 0; out_cleanup: writel(0, dev->base + IMR); /* paranoia */ writel(0, dev->base + IER); readl(dev->base + IER); out_free_irq: rtnl_unlock(); free_irq(pci_dev->irq, ndev); out_disable: if (dev->base) iounmap(dev->base); pci_free_consistent(pci_dev, 4 * DESC_SIZE * NR_TX_DESC, dev->tx_descs, dev->tx_phy_descs); pci_free_consistent(pci_dev, 4 * DESC_SIZE * NR_RX_DESC, dev->rx_info.descs, dev->rx_info.phy_descs); pci_disable_device(pci_dev); out_free: free_netdev(ndev); pci_set_drvdata(pci_dev, NULL); out: return err; } static void __devexit ns83820_remove_one(struct pci_dev *pci_dev) { struct net_device *ndev = pci_get_drvdata(pci_dev); struct ns83820 *dev = PRIV(ndev); /* ok even if NULL */ if (!ndev) /* paranoia */ return; writel(0, dev->base + IMR); /* paranoia */ writel(0, dev->base + IER); readl(dev->base + IER); unregister_netdev(ndev); free_irq(dev->pci_dev->irq, ndev); iounmap(dev->base); pci_free_consistent(dev->pci_dev, 4 * DESC_SIZE * NR_TX_DESC, dev->tx_descs, dev->tx_phy_descs); pci_free_consistent(dev->pci_dev, 4 * DESC_SIZE * NR_RX_DESC, dev->rx_info.descs, dev->rx_info.phy_descs); pci_disable_device(dev->pci_dev); free_netdev(ndev); pci_set_drvdata(pci_dev, NULL); } static struct pci_device_id ns83820_pci_tbl[] = { { 0x100b, 0x0022, PCI_ANY_ID, PCI_ANY_ID, 0, .driver_data = 0, }, { 0, }, }; static struct pci_driver driver = { .name = "ns83820", .id_table = ns83820_pci_tbl, .probe = ns83820_init_one, .remove = __devexit_p(ns83820_remove_one), #if 0 /* FIXME: implement */ .suspend = , .resume = , #endif }; static int __init ns83820_init(void) { printk(KERN_INFO "ns83820.c: National Semiconductor DP83820 10/100/1000 driver.\n"); return pci_register_driver(&driver); } static void __exit ns83820_exit(void) { pci_unregister_driver(&driver); } MODULE_AUTHOR("Benjamin LaHaise <bcrl@kvack.org>"); MODULE_DESCRIPTION("National Semiconductor DP83820 10/100/1000 driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, ns83820_pci_tbl); module_param(lnksts, int, 0); MODULE_PARM_DESC(lnksts, "Polarity of LNKSTS bit"); module_param(ihr, int, 0); MODULE_PARM_DESC(ihr, "Time in 100 us increments to delay interrupts (range 0-127)"); module_param(reset_phy, int, 0); MODULE_PARM_DESC(reset_phy, "Set to 1 to reset the PHY on startup"); module_init(ns83820_init); module_exit(ns83820_exit);