aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/net/slhc.c
blob: c6fbb1ede0ed23e66dbd6a1941a145689622cef6 (plain) (tree)
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































































































































































































































































































































































































































































































































































































































































































































































































                                                                                                 
/*
 * Routines to compress and uncompress tcp packets (for transmission
 * over low speed serial lines).
 *
 * Copyright (c) 1989 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 *	Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
 *	- Initial distribution.
 *
 *
 * modified for KA9Q Internet Software Package by
 * Katie Stevens (dkstevens@ucdavis.edu)
 * University of California, Davis
 * Computing Services
 *	- 01-31-90	initial adaptation (from 1.19)
 *	PPP.05	02-15-90 [ks]
 *	PPP.08	05-02-90 [ks]	use PPP protocol field to signal compression
 *	PPP.15	09-90	 [ks]	improve mbuf handling
 *	PPP.16	11-02	 [karn]	substantially rewritten to use NOS facilities
 *
 *	- Feb 1991	Bill_Simpson@um.cc.umich.edu
 *			variable number of conversation slots
 *			allow zero or one slots
 *			separate routines
 *			status display
 *	- Jul 1994	Dmitry Gorodchanin
 *			Fixes for memory leaks.
 *      - Oct 1994      Dmitry Gorodchanin
 *                      Modularization.
 *	- Jan 1995	Bjorn Ekwall
 *			Use ip_fast_csum from ip.h
 *	- July 1995	Christos A. Polyzols 
 *			Spotted bug in tcp option checking
 *
 *
 *	This module is a difficult issue. It's clearly inet code but it's also clearly
 *	driver code belonging close to PPP and SLIP
 */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <net/slhc_vj.h>

#ifdef CONFIG_INET
/* Entire module is for IP only */
#include <linux/mm.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/termios.h>
#include <linux/in.h>
#include <linux/fcntl.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/icmp.h>
#include <net/tcp.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <linux/timer.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <net/checksum.h>
#include <asm/unaligned.h>

static unsigned char *encode(unsigned char *cp, unsigned short n);
static long decode(unsigned char **cpp);
static unsigned char * put16(unsigned char *cp, unsigned short x);
static unsigned short pull16(unsigned char **cpp);

/* Initialize compression data structure
 *	slots must be in range 0 to 255 (zero meaning no compression)
 */
struct slcompress *
slhc_init(int rslots, int tslots)
{
	register short i;
	register struct cstate *ts;
	struct slcompress *comp;

	comp = (struct slcompress *)kmalloc(sizeof(struct slcompress),
					    GFP_KERNEL);
	if (! comp)
		goto out_fail;
	memset(comp, 0, sizeof(struct slcompress));

	if ( rslots > 0  &&  rslots < 256 ) {
		size_t rsize = rslots * sizeof(struct cstate);
		comp->rstate = (struct cstate *) kmalloc(rsize, GFP_KERNEL);
		if (! comp->rstate)
			goto out_free;
		memset(comp->rstate, 0, rsize);
		comp->rslot_limit = rslots - 1;
	}

	if ( tslots > 0  &&  tslots < 256 ) {
		size_t tsize = tslots * sizeof(struct cstate);
		comp->tstate = (struct cstate *) kmalloc(tsize, GFP_KERNEL);
		if (! comp->tstate)
			goto out_free2;
		memset(comp->tstate, 0, tsize);
		comp->tslot_limit = tslots - 1;
	}

	comp->xmit_oldest = 0;
	comp->xmit_current = 255;
	comp->recv_current = 255;
	/*
	 * don't accept any packets with implicit index until we get
	 * one with an explicit index.  Otherwise the uncompress code
	 * will try to use connection 255, which is almost certainly
	 * out of range
	 */
	comp->flags |= SLF_TOSS;

	if ( tslots > 0 ) {
		ts = comp->tstate;
		for(i = comp->tslot_limit; i > 0; --i){
			ts[i].cs_this = i;
			ts[i].next = &(ts[i - 1]);
		}
		ts[0].next = &(ts[comp->tslot_limit]);
		ts[0].cs_this = 0;
	}
	return comp;

out_free2:
	kfree((unsigned char *)comp->rstate);
out_free:
	kfree((unsigned char *)comp);
out_fail:
	return NULL;
}


/* Free a compression data structure */
void
slhc_free(struct slcompress *comp)
{
	if ( comp == NULLSLCOMPR )
		return;

	if ( comp->tstate != NULLSLSTATE )
		kfree( comp->tstate );

	if ( comp->rstate != NULLSLSTATE )
		kfree( comp->rstate );

	kfree( comp );
}


/* Put a short in host order into a char array in network order */
static inline unsigned char *
put16(unsigned char *cp, unsigned short x)
{
	*cp++ = x >> 8;
	*cp++ = x;

	return cp;
}


/* Encode a number */
unsigned char *
encode(unsigned char *cp, unsigned short n)
{
	if(n >= 256 || n == 0){
		*cp++ = 0;
		cp = put16(cp,n);
	} else {
		*cp++ = n;
	}
	return cp;
}

/* Pull a 16-bit integer in host order from buffer in network byte order */
static unsigned short
pull16(unsigned char **cpp)
{
	short rval;

	rval = *(*cpp)++;
	rval <<= 8;
	rval |= *(*cpp)++;
	return rval;
}

/* Decode a number */
long
decode(unsigned char **cpp)
{
	register int x;

	x = *(*cpp)++;
	if(x == 0){
		return pull16(cpp) & 0xffff;	/* pull16 returns -1 on error */
	} else {
		return x & 0xff;		/* -1 if PULLCHAR returned error */
	}
}

/*
 * icp and isize are the original packet.
 * ocp is a place to put a copy if necessary.
 * cpp is initially a pointer to icp.  If the copy is used,
 *    change it to ocp.
 */

int
slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
	unsigned char *ocp, unsigned char **cpp, int compress_cid)
{
	register struct cstate *ocs = &(comp->tstate[comp->xmit_oldest]);
	register struct cstate *lcs = ocs;
	register struct cstate *cs = lcs->next;
	register unsigned long deltaS, deltaA;
	register short changes = 0;
	int hlen;
	unsigned char new_seq[16];
	register unsigned char *cp = new_seq;
	struct iphdr *ip;
	struct tcphdr *th, *oth;


	/*
	 *	Don't play with runt packets.
	 */
	 
	if(isize<sizeof(struct iphdr))
		return isize;
		
	ip = (struct iphdr *) icp;

	/* Bail if this packet isn't TCP, or is an IP fragment */
	if (ip->protocol != IPPROTO_TCP || (ntohs(ip->frag_off) & 0x3fff)) {
		/* Send as regular IP */
		if(ip->protocol != IPPROTO_TCP)
			comp->sls_o_nontcp++;
		else
			comp->sls_o_tcp++;
		return isize;
	}
	/* Extract TCP header */

	th = (struct tcphdr *)(((unsigned char *)ip) + ip->ihl*4);
	hlen = ip->ihl*4 + th->doff*4;

	/*  Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or
	 *  some other control bit is set). Also uncompressible if
	 *  it's a runt.
	 */
	if(hlen > isize || th->syn || th->fin || th->rst ||
	    ! (th->ack)){
		/* TCP connection stuff; send as regular IP */
		comp->sls_o_tcp++;
		return isize;
	}
	/*
	 * Packet is compressible -- we're going to send either a
	 * COMPRESSED_TCP or UNCOMPRESSED_TCP packet.  Either way,
	 * we need to locate (or create) the connection state.
	 *
	 * States are kept in a circularly linked list with
	 * xmit_oldest pointing to the end of the list.  The
	 * list is kept in lru order by moving a state to the
	 * head of the list whenever it is referenced.  Since
	 * the list is short and, empirically, the connection
	 * we want is almost always near the front, we locate
	 * states via linear search.  If we don't find a state
	 * for the datagram, the oldest state is (re-)used.
	 */
	for ( ; ; ) {
		if( ip->saddr == cs->cs_ip.saddr
		 && ip->daddr == cs->cs_ip.daddr
		 && th->source == cs->cs_tcp.source
		 && th->dest == cs->cs_tcp.dest)
			goto found;

		/* if current equal oldest, at end of list */
		if ( cs == ocs )
			break;
		lcs = cs;
		cs = cs->next;
		comp->sls_o_searches++;
	};
	/*
	 * Didn't find it -- re-use oldest cstate.  Send an
	 * uncompressed packet that tells the other side what
	 * connection number we're using for this conversation.
	 *
	 * Note that since the state list is circular, the oldest
	 * state points to the newest and we only need to set
	 * xmit_oldest to update the lru linkage.
	 */
	comp->sls_o_misses++;
	comp->xmit_oldest = lcs->cs_this;
	goto uncompressed;

found:
	/*
	 * Found it -- move to the front on the connection list.
	 */
	if(lcs == ocs) {
 		/* found at most recently used */
	} else if (cs == ocs) {
		/* found at least recently used */
		comp->xmit_oldest = lcs->cs_this;
	} else {
		/* more than 2 elements */
		lcs->next = cs->next;
		cs->next = ocs->next;
		ocs->next = cs;
	}

	/*
	 * Make sure that only what we expect to change changed.
	 * Check the following:
	 * IP protocol version, header length & type of service.
	 * The "Don't fragment" bit.
	 * The time-to-live field.
	 * The TCP header length.
	 * IP options, if any.
	 * TCP options, if any.
	 * If any of these things are different between the previous &
	 * current datagram, we send the current datagram `uncompressed'.
	 */
	oth = &cs->cs_tcp;

	if(ip->version != cs->cs_ip.version || ip->ihl != cs->cs_ip.ihl
	 || ip->tos != cs->cs_ip.tos
	 || (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))
	 || ip->ttl != cs->cs_ip.ttl
	 || th->doff != cs->cs_tcp.doff
	 || (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)
	 || (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)){
		goto uncompressed;
	}

	/*
	 * Figure out which of the changing fields changed.  The
	 * receiver expects changes in the order: urgent, window,
	 * ack, seq (the order minimizes the number of temporaries
	 * needed in this section of code).
	 */
	if(th->urg){
		deltaS = ntohs(th->urg_ptr);
		cp = encode(cp,deltaS);
		changes |= NEW_U;
	} else if(th->urg_ptr != oth->urg_ptr){
		/* argh! URG not set but urp changed -- a sensible
		 * implementation should never do this but RFC793
		 * doesn't prohibit the change so we have to deal
		 * with it. */
		goto uncompressed;
	}
	if((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0){
		cp = encode(cp,deltaS);
		changes |= NEW_W;
	}
	if((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L){
		if(deltaA > 0x0000ffff)
			goto uncompressed;
		cp = encode(cp,deltaA);
		changes |= NEW_A;
	}
	if((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L){
		if(deltaS > 0x0000ffff)
			goto uncompressed;
		cp = encode(cp,deltaS);
		changes |= NEW_S;
	}

	switch(changes){
	case 0:	/* Nothing changed. If this packet contains data and the
		 * last one didn't, this is probably a data packet following
		 * an ack (normal on an interactive connection) and we send
		 * it compressed.  Otherwise it's probably a retransmit,
		 * retransmitted ack or window probe.  Send it uncompressed
		 * in case the other side missed the compressed version.
		 */
		if(ip->tot_len != cs->cs_ip.tot_len &&
		   ntohs(cs->cs_ip.tot_len) == hlen)
			break;
		goto uncompressed;
		break;
	case SPECIAL_I:
	case SPECIAL_D:
		/* actual changes match one of our special case encodings --
		 * send packet uncompressed.
		 */
		goto uncompressed;
	case NEW_S|NEW_A:
		if(deltaS == deltaA &&
		    deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
			/* special case for echoed terminal traffic */
			changes = SPECIAL_I;
			cp = new_seq;
		}
		break;
	case NEW_S:
		if(deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
			/* special case for data xfer */
			changes = SPECIAL_D;
			cp = new_seq;
		}
		break;
	}
	deltaS = ntohs(ip->id) - ntohs(cs->cs_ip.id);
	if(deltaS != 1){
		cp = encode(cp,deltaS);
		changes |= NEW_I;
	}
	if(th->psh)
		changes |= TCP_PUSH_BIT;
	/* Grab the cksum before we overwrite it below.  Then update our
	 * state with this packet's header.
	 */
	deltaA = ntohs(th->check);
	memcpy(&cs->cs_ip,ip,20);
	memcpy(&cs->cs_tcp,th,20);
	/* We want to use the original packet as our compressed packet.
	 * (cp - new_seq) is the number of bytes we need for compressed
	 * sequence numbers.  In addition we need one byte for the change
	 * mask, one for the connection id and two for the tcp checksum.
	 * So, (cp - new_seq) + 4 bytes of header are needed.
	 */
	deltaS = cp - new_seq;
	if(compress_cid == 0 || comp->xmit_current != cs->cs_this){
		cp = ocp;
		*cpp = ocp;
		*cp++ = changes | NEW_C;
		*cp++ = cs->cs_this;
		comp->xmit_current = cs->cs_this;
	} else {
		cp = ocp;
		*cpp = ocp;
		*cp++ = changes;
	}
	cp = put16(cp,(short)deltaA);	/* Write TCP checksum */
/* deltaS is now the size of the change section of the compressed header */
	memcpy(cp,new_seq,deltaS);	/* Write list of deltas */
	memcpy(cp+deltaS,icp+hlen,isize-hlen);
	comp->sls_o_compressed++;
	ocp[0] |= SL_TYPE_COMPRESSED_TCP;
	return isize - hlen + deltaS + (cp - ocp);

	/* Update connection state cs & send uncompressed packet (i.e.,
	 * a regular ip/tcp packet but with the 'conversation id' we hope
	 * to use on future compressed packets in the protocol field).
	 */
uncompressed:
	memcpy(&cs->cs_ip,ip,20);
	memcpy(&cs->cs_tcp,th,20);
	if (ip->ihl > 5)
	  memcpy(cs->cs_ipopt, ip+1, ((ip->ihl) - 5) * 4);
	if (th->doff > 5)
	  memcpy(cs->cs_tcpopt, th+1, ((th->doff) - 5) * 4);
	comp->xmit_current = cs->cs_this;
	comp->sls_o_uncompressed++;
	memcpy(ocp, icp, isize);
	*cpp = ocp;
	ocp[9] = cs->cs_this;
	ocp[0] |= SL_TYPE_UNCOMPRESSED_TCP;
	return isize;
}


int
slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
{
	register int changes;
	long x;
	register struct tcphdr *thp;
	register struct iphdr *ip;
	register struct cstate *cs;
	int len, hdrlen;
	unsigned char *cp = icp;

	/* We've got a compressed packet; read the change byte */
	comp->sls_i_compressed++;
	if(isize < 3){
		comp->sls_i_error++;
		return 0;
	}
	changes = *cp++;
	if(changes & NEW_C){
		/* Make sure the state index is in range, then grab the state.
		 * If we have a good state index, clear the 'discard' flag.
		 */
		x = *cp++;	/* Read conn index */
		if(x < 0 || x > comp->rslot_limit)
			goto bad;

		comp->flags &=~ SLF_TOSS;
		comp->recv_current = x;
	} else {
		/* this packet has an implicit state index.  If we've
		 * had a line error since the last time we got an
		 * explicit state index, we have to toss the packet. */
		if(comp->flags & SLF_TOSS){
			comp->sls_i_tossed++;
			return 0;
		}
	}
	cs = &comp->rstate[comp->recv_current];
	thp = &cs->cs_tcp;
	ip = &cs->cs_ip;

	if((x = pull16(&cp)) == -1) {	/* Read the TCP checksum */
		goto bad;
        }
	thp->check = htons(x);

	thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0;
/*
 * we can use the same number for the length of the saved header and
 * the current one, because the packet wouldn't have been sent
 * as compressed unless the options were the same as the previous one
 */

	hdrlen = ip->ihl * 4 + thp->doff * 4;

	switch(changes & SPECIALS_MASK){
	case SPECIAL_I:		/* Echoed terminal traffic */
		{
		register short i;
		i = ntohs(ip->tot_len) - hdrlen;
		thp->ack_seq = htonl( ntohl(thp->ack_seq) + i);
		thp->seq = htonl( ntohl(thp->seq) + i);
		}
		break;

	case SPECIAL_D:			/* Unidirectional data */
		thp->seq = htonl( ntohl(thp->seq) +
				  ntohs(ip->tot_len) - hdrlen);
		break;

	default:
		if(changes & NEW_U){
			thp->urg = 1;
			if((x = decode(&cp)) == -1) {
				goto bad;
			}
			thp->urg_ptr = htons(x);
		} else
			thp->urg = 0;
		if(changes & NEW_W){
			if((x = decode(&cp)) == -1) {
				goto bad;
			}
			thp->window = htons( ntohs(thp->window) + x);
		}
		if(changes & NEW_A){
			if((x = decode(&cp)) == -1) {
				goto bad;
			}
			thp->ack_seq = htonl( ntohl(thp->ack_seq) + x);
		}
		if(changes & NEW_S){
			if((x = decode(&cp)) == -1) {
				goto bad;
			}
			thp->seq = htonl( ntohl(thp->seq) + x);
		}
		break;
	}
	if(changes & NEW_I){
		if((x = decode(&cp)) == -1) {
			goto bad;
		}
		ip->id = htons (ntohs (ip->id) + x);
	} else
		ip->id = htons (ntohs (ip->id) + 1);

	/*
	 * At this point, cp points to the first byte of data in the
	 * packet.  Put the reconstructed TCP and IP headers back on the
	 * packet.  Recalculate IP checksum (but not TCP checksum).
	 */

	len = isize - (cp - icp);
	if (len < 0)
		goto bad;
	len += hdrlen;
	ip->tot_len = htons(len);
	ip->check = 0;

	memmove(icp + hdrlen, cp, len - hdrlen);

	cp = icp;
	memcpy(cp, ip, 20);
	cp += 20;

	if (ip->ihl > 5) {
	  memcpy(cp, cs->cs_ipopt, (ip->ihl - 5) * 4);
	  cp += (ip->ihl - 5) * 4;
	}

	put_unaligned(ip_fast_csum(icp, ip->ihl),
		      &((struct iphdr *)icp)->check);

	memcpy(cp, thp, 20);
	cp += 20;

	if (thp->doff > 5) {
	  memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * 4);
	  cp += ((thp->doff) - 5) * 4;
	}

	return len;
bad:
	comp->sls_i_error++;
	return slhc_toss( comp );
}


int
slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
{
	register struct cstate *cs;
	unsigned ihl;

	unsigned char index;

	if(isize < 20) {
		/* The packet is shorter than a legal IP header */
		comp->sls_i_runt++;
		return slhc_toss( comp );
	}
	/* Peek at the IP header's IHL field to find its length */
	ihl = icp[0] & 0xf;
	if(ihl < 20 / 4){
		/* The IP header length field is too small */
		comp->sls_i_runt++;
		return slhc_toss( comp );
	}
	index = icp[9];
	icp[9] = IPPROTO_TCP;

	if (ip_fast_csum(icp, ihl)) {
		/* Bad IP header checksum; discard */
		comp->sls_i_badcheck++;
		return slhc_toss( comp );
	}
	if(index > comp->rslot_limit) {
		comp->sls_i_error++;
		return slhc_toss(comp);
	}

	/* Update local state */
	cs = &comp->rstate[comp->recv_current = index];
	comp->flags &=~ SLF_TOSS;
	memcpy(&cs->cs_ip,icp,20);
	memcpy(&cs->cs_tcp,icp + ihl*4,20);
	if (ihl > 5)
	  memcpy(cs->cs_ipopt, icp + sizeof(struct iphdr), (ihl - 5) * 4);
	if (cs->cs_tcp.doff > 5)
	  memcpy(cs->cs_tcpopt, icp + ihl*4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4);
	cs->cs_hsize = ihl*2 + cs->cs_tcp.doff*2;
	/* Put headers back on packet
	 * Neither header checksum is recalculated
	 */
	comp->sls_i_uncompressed++;
	return isize;
}

int
slhc_toss(struct slcompress *comp)
{
	if ( comp == NULLSLCOMPR )
		return 0;

	comp->flags |= SLF_TOSS;
	return 0;
}


/* VJ header compression */
EXPORT_SYMBOL(slhc_init);
EXPORT_SYMBOL(slhc_free);
EXPORT_SYMBOL(slhc_remember);
EXPORT_SYMBOL(slhc_compress);
EXPORT_SYMBOL(slhc_uncompress);
EXPORT_SYMBOL(slhc_toss);

#ifdef MODULE

int init_module(void)
{
	printk(KERN_INFO "CSLIP: code copyright 1989 Regents of the University of California\n");
	return 0;
}

void cleanup_module(void)
{
	return;
}

#endif /* MODULE */
#else /* CONFIG_INET */


int
slhc_toss(struct slcompress *comp)
{
  printk(KERN_DEBUG "Called IP function on non IP-system: slhc_toss");
  return -EINVAL;
}
int
slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
{
  printk(KERN_DEBUG "Called IP function on non IP-system: slhc_uncompress");
  return -EINVAL;
}
int
slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
	unsigned char *ocp, unsigned char **cpp, int compress_cid)
{
  printk(KERN_DEBUG "Called IP function on non IP-system: slhc_compress");
  return -EINVAL;
}

int
slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
{
  printk(KERN_DEBUG "Called IP function on non IP-system: slhc_remember");
  return -EINVAL;
}

void
slhc_free(struct slcompress *comp)
{
  printk(KERN_DEBUG "Called IP function on non IP-system: slhc_free");
  return;
}
struct slcompress *
slhc_init(int rslots, int tslots)
{
  printk(KERN_DEBUG "Called IP function on non IP-system: slhc_init");
  return NULL;
}
EXPORT_SYMBOL(slhc_init);
EXPORT_SYMBOL(slhc_free);
EXPORT_SYMBOL(slhc_remember);
EXPORT_SYMBOL(slhc_compress);
EXPORT_SYMBOL(slhc_uncompress);
EXPORT_SYMBOL(slhc_toss);

#endif /* CONFIG_INET */
MODULE_LICENSE("Dual BSD/GPL");
LM 0x02000000UL #define IMR_UDPIM 0x00400000UL #define IMR_MIBFIM 0x00200000UL #define IMR_SHDNIM 0x00100000UL #define IMR_PHYIM 0x00080000UL #define IMR_PWEIM 0x00040000UL #define IMR_TMR1IM 0x00020000UL #define IMR_TMR0IM 0x00010000UL #define IMR_SRCIM 0x00008000UL #define IMR_LSTPEIM 0x00004000UL #define IMR_LSTEIM 0x00002000UL #define IMR_OVFIM 0x00001000UL #define IMR_FLONIM 0x00000800UL #define IMR_RACEIM 0x00000400UL #define IMR_TXWB1IM 0x00000200UL #define IMR_TXWB0IM 0x00000100UL #define IMR_PTX3IM 0x00000080UL #define IMR_PTX2IM 0x00000040UL #define IMR_PTX1IM 0x00000020UL #define IMR_PTX0IM 0x00000010UL #define IMR_PTXIM 0x00000008UL #define IMR_PRXIM 0x00000004UL #define IMR_PPTXIM 0x00000002UL #define IMR_PPRXIM 0x00000001UL /* 0x0013FB0FUL = initial value of IMR */ #define INT_MASK_DEF (IMR_PPTXIM|IMR_PPRXIM|IMR_PTXIM|IMR_PRXIM|\ IMR_PWEIM|IMR_TXWB0IM|IMR_TXWB1IM|IMR_FLONIM|\ IMR_OVFIM|IMR_LSTEIM|IMR_LSTPEIM|IMR_SRCIM|IMR_MIBFIM|\ IMR_SHDNIM|IMR_TMR1IM|IMR_TMR0IM|IMR_TXSTLM) /* * Bits in the TDCSR0/1, RDCSR0 register */ #define TRDCSR_DEAD 0x0008 #define TRDCSR_WAK 0x0004 #define TRDCSR_ACT 0x0002 #define TRDCSR_RUN 0x0001 /* * Bits in the CAMADDR register */ #define CAMADDR_CAMEN 0x80 #define CAMADDR_VCAMSL 0x40 /* * Bits in the CAMCR register */ #define CAMCR_PS1 0x80 #define CAMCR_PS0 0x40 #define CAMCR_AITRPKT 0x20 #define CAMCR_AITR16 0x10 #define CAMCR_CAMRD 0x08 #define CAMCR_CAMWR 0x04 #define CAMCR_PS_CAM_MASK 0x40 #define CAMCR_PS_CAM_DATA 0x80 #define CAMCR_PS_MAR 0x00 /* * Bits in the MIICFG register */ #define MIICFG_MPO1 0x80 #define MIICFG_MPO0 0x40 #define MIICFG_MFDC 0x20 /* * Bits in the MIISR register */ #define MIISR_MIDLE 0x80 /* * Bits in the PHYSR0 register */ #define PHYSR0_PHYRST 0x80 #define PHYSR0_LINKGD 0x40 #define PHYSR0_FDPX 0x10 #define PHYSR0_SPDG 0x08 #define PHYSR0_SPD10 0x04 #define PHYSR0_RXFLC 0x02 #define PHYSR0_TXFLC 0x01 /* * Bits in the PHYSR1 register */ #define PHYSR1_PHYTBI 0x01 /* * Bits in the MIICR register */ #define MIICR_MAUTO 0x80 #define MIICR_RCMD 0x40 #define MIICR_WCMD 0x20 #define MIICR_MDPM 0x10 #define MIICR_MOUT 0x08 #define MIICR_MDO 0x04 #define MIICR_MDI 0x02 #define MIICR_MDC 0x01 /* * Bits in the MIIADR register */ #define MIIADR_SWMPL 0x80 /* * Bits in the CFGA register */ #define CFGA_PMHCTG 0x08 #define CFGA_GPIO1PD 0x04 #define CFGA_ABSHDN 0x02 #define CFGA_PACPI 0x01 /* * Bits in the CFGB register */ #define CFGB_GTCKOPT 0x80 #define CFGB_MIIOPT 0x40 #define CFGB_CRSEOPT 0x20 #define CFGB_OFSET 0x10 #define CFGB_CRANDOM 0x08 #define CFGB_CAP 0x04 #define CFGB_MBA 0x02 #define CFGB_BAKOPT 0x01 /* * Bits in the CFGC register */ #define CFGC_EELOAD 0x80 #define CFGC_BROPT 0x40 #define CFGC_DLYEN 0x20 #define CFGC_DTSEL 0x10 #define CFGC_BTSEL 0x08 #define CFGC_BPS2 0x04 /* bootrom select[2] */ #define CFGC_BPS1 0x02 /* bootrom select[1] */ #define CFGC_BPS0 0x01 /* bootrom select[0] */ /* * Bits in the CFGD register */ #define CFGD_IODIS 0x80 #define CFGD_MSLVDACEN 0x40 #define CFGD_CFGDACEN 0x20 #define CFGD_PCI64EN 0x10 #define CFGD_HTMRL4 0x08 /* * Bits in the DCFG1 register */ #define DCFG_XMWI 0x8000 #define DCFG_XMRM 0x4000 #define DCFG_XMRL 0x2000 #define DCFG_PERDIS 0x1000 #define DCFG_MRWAIT 0x0400 #define DCFG_MWWAIT 0x0200 #define DCFG_LATMEN 0x0100 /* * Bits in the MCFG0 register */ #define MCFG_RXARB 0x0080 #define MCFG_RFT1 0x0020 #define MCFG_RFT0 0x0010 #define MCFG_LOWTHOPT 0x0008 #define MCFG_PQEN 0x0004 #define MCFG_RTGOPT 0x0002 #define MCFG_VIDFR 0x0001 /* * Bits in the MCFG1 register */ #define MCFG_TXARB 0x8000 #define MCFG_TXQBK1 0x0800 #define MCFG_TXQBK0 0x0400 #define MCFG_TXQNOBK 0x0200 #define MCFG_SNAPOPT 0x0100 /* * Bits in the PMCC register */ #define PMCC_DSI 0x80 #define PMCC_D2_DIS 0x40 #define PMCC_D1_DIS 0x20 #define PMCC_D3C_EN 0x10 #define PMCC_D3H_EN 0x08 #define PMCC_D2_EN 0x04 #define PMCC_D1_EN 0x02 #define PMCC_D0_EN 0x01 /* * Bits in STICKHW */ #define STICKHW_SWPTAG 0x10 #define STICKHW_WOLSR 0x08 #define STICKHW_WOLEN 0x04 #define STICKHW_DS1 0x02 /* R/W by software/cfg cycle */ #define STICKHW_DS0 0x01 /* suspend well DS write port */ /* * Bits in the MIBCR register */ #define MIBCR_MIBISTOK 0x80 #define MIBCR_MIBISTGO 0x40 #define MIBCR_MIBINC 0x20 #define MIBCR_MIBHI 0x10 #define MIBCR_MIBFRZ 0x08 #define MIBCR_MIBFLSH 0x04 #define MIBCR_MPTRINI 0x02 #define MIBCR_MIBCLR 0x01 /* * Bits in the EERSV register */ #define EERSV_BOOT_RPL ((u8) 0x01) /* Boot method selection for VT6110 */ #define EERSV_BOOT_MASK ((u8) 0x06) #define EERSV_BOOT_INT19 ((u8) 0x00) #define EERSV_BOOT_INT18 ((u8) 0x02) #define EERSV_BOOT_LOCAL ((u8) 0x04) #define EERSV_BOOT_BEV ((u8) 0x06) /* * Bits in BPCMD */ #define BPCMD_BPDNE 0x80 #define BPCMD_EBPWR 0x02 #define BPCMD_EBPRD 0x01 /* * Bits in the EECSR register */ #define EECSR_EMBP 0x40 /* eeprom embeded programming */ #define EECSR_RELOAD 0x20 /* eeprom content reload */ #define EECSR_DPM 0x10 /* eeprom direct programming */ #define EECSR_ECS 0x08 /* eeprom CS pin */ #define EECSR_ECK 0x04 /* eeprom CK pin */ #define EECSR_EDI 0x02 /* eeprom DI pin */ #define EECSR_EDO 0x01 /* eeprom DO pin */ /* * Bits in the EMBCMD register */ #define EMBCMD_EDONE 0x80 #define EMBCMD_EWDIS 0x08 #define EMBCMD_EWEN 0x04 #define EMBCMD_EWR 0x02 #define EMBCMD_ERD 0x01 /* * Bits in TESTCFG register */ #define TESTCFG_HBDIS 0x80 /* * Bits in CHIPGCR register */ #define CHIPGCR_FCGMII 0x80 #define CHIPGCR_FCFDX 0x40 #define CHIPGCR_FCRESV 0x20 #define CHIPGCR_FCMODE 0x10 #define CHIPGCR_LPSOPT 0x08 #define CHIPGCR_TM1US 0x04 #define CHIPGCR_TM0US 0x02 #define CHIPGCR_PHYINTEN 0x01 /* * Bits in WOLCR0 */ #define WOLCR_MSWOLEN7 0x0080 /* enable pattern match filtering */ #define WOLCR_MSWOLEN6 0x0040 #define WOLCR_MSWOLEN5 0x0020 #define WOLCR_MSWOLEN4 0x0010 #define WOLCR_MSWOLEN3 0x0008 #define WOLCR_MSWOLEN2 0x0004 #define WOLCR_MSWOLEN1 0x0002 #define WOLCR_MSWOLEN0 0x0001 #define WOLCR_ARP_EN 0x0001 /* * Bits in WOLCR1 */ #define WOLCR_LINKOFF_EN 0x0800 /* link off detected enable */ #define WOLCR_LINKON_EN 0x0400 /* link on detected enable */ #define WOLCR_MAGIC_EN 0x0200 /* magic packet filter enable */ #define WOLCR_UNICAST_EN 0x0100 /* unicast filter enable */ /* * Bits in PWCFG */ #define PWCFG_PHYPWOPT 0x80 /* internal MII I/F timing */ #define PWCFG_PCISTICK 0x40 /* PCI sticky R/W enable */ #define PWCFG_WOLTYPE 0x20 /* pulse(1) or button (0) */ #define PWCFG_LEGCY_WOL 0x10 #define PWCFG_PMCSR_PME_SR 0x08 #define PWCFG_PMCSR_PME_EN 0x04 /* control by PCISTICK */ #define PWCFG_LEGACY_WOLSR 0x02 /* Legacy WOL_SR shadow */ #define PWCFG_LEGACY_WOLEN 0x01 /* Legacy WOL_EN shadow */ /* * Bits in WOLCFG */ #define WOLCFG_PMEOVR 0x80 /* for legacy use, force PMEEN always */ #define WOLCFG_SAM 0x20 /* accept multicast case reset, default=0 */ #define WOLCFG_SAB 0x10 /* accept broadcast case reset, default=0 */ #define WOLCFG_SMIIACC 0x08 /* ?? */ #define WOLCFG_SGENWH 0x02 #define WOLCFG_PHYINTEN 0x01 /* 0:PHYINT trigger enable, 1:use internal MII to report status change */ /* * Bits in WOLSR1 */ #define WOLSR_LINKOFF_INT 0x0800 #define WOLSR_LINKON_INT 0x0400 #define WOLSR_MAGIC_INT 0x0200 #define WOLSR_UNICAST_INT 0x0100 /* * Ethernet address filter type */ #define PKT_TYPE_NONE 0x0000 /* Turn off receiver */ #define PKT_TYPE_DIRECTED 0x0001 /* obselete, directed address is always accepted */ #define PKT_TYPE_MULTICAST 0x0002 #define PKT_TYPE_ALL_MULTICAST 0x0004 #define PKT_TYPE_BROADCAST 0x0008 #define PKT_TYPE_PROMISCUOUS 0x0020 #define PKT_TYPE_LONG 0x2000 /* NOTE.... the definition of LONG is >2048 bytes in our chip */ #define PKT_TYPE_RUNT 0x4000 #define PKT_TYPE_ERROR 0x8000 /* Accept error packets, e.g. CRC error */ /* * Loopback mode */ #define MAC_LB_NONE 0x00 #define MAC_LB_INTERNAL 0x01 #define MAC_LB_EXTERNAL 0x02 /* * Enabled mask value of irq */ #if defined(_SIM) #define IMR_MASK_VALUE 0x0033FF0FUL /* initial value of IMR set IMR0 to 0x0F according to spec */ #else #define IMR_MASK_VALUE 0x0013FB0FUL /* initial value of IMR ignore MIBFI,RACEI to reduce intr. frequency NOTE.... do not enable NoBuf int mask at driver driver when (1) NoBuf -> RxThreshold = SF (2) OK -> RxThreshold = original value */ #endif /* * Revision id */ #define REV_ID_VT3119_A0 0x00 #define REV_ID_VT3119_A1 0x01 #define REV_ID_VT3216_A0 0x10 /* * Max time out delay time */ #define W_MAX_TIMEOUT 0x0FFFU /* * MAC registers as a structure. Cannot be directly accessed this * way but generates offsets for readl/writel() calls */ struct mac_regs { volatile u8 PAR[6]; /* 0x00 */ volatile u8 RCR; volatile u8 TCR; volatile u32 CR0Set; /* 0x08 */ volatile u32 CR0Clr; /* 0x0C */ volatile u8 MARCAM[8]; /* 0x10 */ volatile u32 DecBaseHi; /* 0x18 */ volatile u16 DbfBaseHi; /* 0x1C */ volatile u16 reserved_1E; volatile u16 ISRCTL; /* 0x20 */ volatile u8 TXESR; volatile u8 RXESR; volatile u32 ISR; /* 0x24 */ volatile u32 IMR; volatile u32 TDStatusPort; /* 0x2C */ volatile u16 TDCSRSet; /* 0x30 */ volatile u8 RDCSRSet; volatile u8 reserved_33; volatile u16 TDCSRClr; volatile u8 RDCSRClr; volatile u8 reserved_37; volatile u32 RDBaseLo; /* 0x38 */ volatile u16 RDIdx; /* 0x3C */ volatile u16 reserved_3E; volatile u32 TDBaseLo[4]; /* 0x40 */ volatile u16 RDCSize; /* 0x50 */ volatile u16 TDCSize; /* 0x52 */ volatile u16 TDIdx[4]; /* 0x54 */ volatile u16 tx_pause_timer; /* 0x5C */ volatile u16 RBRDU; /* 0x5E */ volatile u32 FIFOTest0; /* 0x60 */ volatile u32 FIFOTest1; /* 0x64 */ volatile u8 CAMADDR; /* 0x68 */ volatile u8 CAMCR; /* 0x69 */ volatile u8 GFTEST; /* 0x6A */ volatile u8 FTSTCMD; /* 0x6B */ volatile u8 MIICFG; /* 0x6C */ volatile u8 MIISR; volatile u8 PHYSR0; volatile u8 PHYSR1; volatile u8 MIICR; volatile u8 MIIADR; volatile u16 MIIDATA; volatile u16 SoftTimer0; /* 0x74 */ volatile u16 SoftTimer1; volatile u8 CFGA; /* 0x78 */ volatile u8 CFGB; volatile u8 CFGC; volatile u8 CFGD; volatile u16 DCFG; /* 0x7C */ volatile u16 MCFG; volatile u8 TBIST; /* 0x80 */ volatile u8 RBIST; volatile u8 PMCPORT; volatile u8 STICKHW; volatile u8 MIBCR; /* 0x84 */ volatile u8 reserved_85; volatile u8 rev_id; volatile u8 PORSTS; volatile u32 MIBData; /* 0x88 */ volatile u16 EEWrData; volatile u8 reserved_8E; volatile u8 BPMDWr; volatile u8 BPCMD; volatile u8 BPMDRd; volatile u8 EECHKSUM; /* 0x92 */ volatile u8 EECSR; volatile u16 EERdData; /* 0x94 */ volatile u8 EADDR; volatile u8 EMBCMD; volatile u8 JMPSR0; /* 0x98 */ volatile u8 JMPSR1; volatile u8 JMPSR2; volatile u8 JMPSR3; volatile u8 CHIPGSR; /* 0x9C */ volatile u8 TESTCFG; volatile u8 DEBUG; volatile u8 CHIPGCR; volatile u16 WOLCRSet; /* 0xA0 */ volatile u8 PWCFGSet; volatile u8 WOLCFGSet; volatile u16 WOLCRClr; /* 0xA4 */ volatile u8 PWCFGCLR; volatile u8 WOLCFGClr; volatile u16 WOLSRSet; /* 0xA8 */ volatile u16 reserved_AA; volatile u16 WOLSRClr; /* 0xAC */ volatile u16 reserved_AE; volatile u16 PatternCRC[8]; /* 0xB0 */ volatile u32 ByteMask[4][4]; /* 0xC0 */ } __attribute__ ((__packed__)); enum hw_mib { HW_MIB_ifRxAllPkts = 0, HW_MIB_ifRxOkPkts, HW_MIB_ifTxOkPkts, HW_MIB_ifRxErrorPkts, HW_MIB_ifRxRuntOkPkt, HW_MIB_ifRxRuntErrPkt, HW_MIB_ifRx64Pkts, HW_MIB_ifTx64Pkts, HW_MIB_ifRx65To127Pkts, HW_MIB_ifTx65To127Pkts, HW_MIB_ifRx128To255Pkts, HW_MIB_ifTx128To255Pkts, HW_MIB_ifRx256To511Pkts, HW_MIB_ifTx256To511Pkts, HW_MIB_ifRx512To1023Pkts, HW_MIB_ifTx512To1023Pkts, HW_MIB_ifRx1024To1518Pkts, HW_MIB_ifTx1024To1518Pkts, HW_MIB_ifTxEtherCollisions, HW_MIB_ifRxPktCRCE, HW_MIB_ifRxJumboPkts, HW_MIB_ifTxJumboPkts, HW_MIB_ifRxMacControlFrames, HW_MIB_ifTxMacControlFrames, HW_MIB_ifRxPktFAE, HW_MIB_ifRxLongOkPkt, HW_MIB_ifRxLongPktErrPkt, HW_MIB_ifTXSQEErrors, HW_MIB_ifRxNobuf, HW_MIB_ifRxSymbolErrors, HW_MIB_ifInRangeLengthErrors, HW_MIB_ifLateCollisions, HW_MIB_SIZE }; enum chip_type { CHIP_TYPE_VT6110 = 1, }; struct velocity_info_tbl { enum chip_type chip_id; char *name; int io_size; int txqueue; u32 flags; }; #define mac_hw_mibs_init(regs) {\ BYTE_REG_BITS_ON(MIBCR_MIBFRZ,&((regs)->MIBCR));\ BYTE_REG_BITS_ON(MIBCR_MIBCLR,&((regs)->MIBCR));\ do {}\ while (BYTE_REG_BITS_IS_ON(MIBCR_MIBCLR,&((regs)->MIBCR)));\ BYTE_REG_BITS_OFF(MIBCR_MIBFRZ,&((regs)->MIBCR));\ } #define mac_read_isr(regs) readl(&((regs)->ISR)) #define mac_write_isr(regs, x) writel((x),&((regs)->ISR)) #define mac_clear_isr(regs) writel(0xffffffffL,&((regs)->ISR)) #define mac_write_int_mask(mask, regs) writel((mask),&((regs)->IMR)); #define mac_disable_int(regs) writel(CR0_GINTMSK1,&((regs)->CR0Clr)) #define mac_enable_int(regs) writel(CR0_GINTMSK1,&((regs)->CR0Set)) #define mac_hw_mibs_read(regs, MIBs) {\ int i;\ BYTE_REG_BITS_ON(MIBCR_MPTRINI,&((regs)->MIBCR));\ for (i=0;i<HW_MIB_SIZE;i++) {\ (MIBs)[i]=readl(&((regs)->MIBData));\ }\ } #define mac_set_dma_length(regs, n) {\ BYTE_REG_BITS_SET((n),0x07,&((regs)->DCFG));\ } #define mac_set_rx_thresh(regs, n) {\ BYTE_REG_BITS_SET((n),(MCFG_RFT0|MCFG_RFT1),&((regs)->MCFG));\ } #define mac_rx_queue_run(regs) {\ writeb(TRDCSR_RUN, &((regs)->RDCSRSet));\ } #define mac_rx_queue_wake(regs) {\ writeb(TRDCSR_WAK, &((regs)->RDCSRSet));\ } #define mac_tx_queue_run(regs, n) {\ writew(TRDCSR_RUN<<((n)*4),&((regs)->TDCSRSet));\ } #define mac_tx_queue_wake(regs, n) {\ writew(TRDCSR_WAK<<(n*4),&((regs)->TDCSRSet));\ } #define mac_eeprom_reload(regs) {\ int i=0;\ BYTE_REG_BITS_ON(EECSR_RELOAD,&((regs)->EECSR));\ do {\ udelay(10);\ if (i++>0x1000) {\ break;\ }\ }while (BYTE_REG_BITS_IS_ON(EECSR_RELOAD,&((regs)->EECSR)));\ } enum velocity_cam_type { VELOCITY_VLAN_ID_CAM = 0, VELOCITY_MULTICAST_CAM }; /** * mac_get_cam_mask - Read a CAM mask * @regs: register block for this velocity * @mask: buffer to store mask * @cam_type: CAM to fetch * * Fetch the mask bits of the selected CAM and store them into the * provided mask buffer. */ static inline void mac_get_cam_mask(struct mac_regs __iomem * regs, u8 * mask, enum velocity_cam_type cam_type) { int i; /* Select CAM mask */ BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR); if (cam_type == VELOCITY_VLAN_ID_CAM) writeb(CAMADDR_VCAMSL, &regs->CAMADDR); else writeb(0, &regs->CAMADDR); /* read mask */ for (i = 0; i < 8; i++) *mask++ = readb(&(regs->MARCAM[i])); /* disable CAMEN */ writeb(0, &regs->CAMADDR); /* Select mar */ BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR); } /** * mac_set_cam_mask - Set a CAM mask * @regs: register block for this velocity * @mask: CAM mask to load * @cam_type: CAM to store * * Store a new mask into a CAM */ static inline void mac_set_cam_mask(struct mac_regs __iomem * regs, u8 * mask, enum velocity_cam_type cam_type) { int i; /* Select CAM mask */ BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR); if (cam_type == VELOCITY_VLAN_ID_CAM) writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL, &regs->CAMADDR); else writeb(CAMADDR_CAMEN, &regs->CAMADDR); for (i = 0; i < 8; i++) { writeb(*mask++, &(regs->MARCAM[i])); } /* disable CAMEN */ writeb(0, &regs->CAMADDR); /* Select mar */ BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR); } /** * mac_set_cam - set CAM data * @regs: register block of this velocity * @idx: Cam index * @addr: 2 or 6 bytes of CAM data * @cam_type: CAM to load * * Load an address or vlan tag into a CAM */ static inline void mac_set_cam(struct mac_regs __iomem * regs, int idx, u8 *addr, enum velocity_cam_type cam_type) { int i; /* Select CAM mask */ BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR); idx &= (64 - 1); if (cam_type == VELOCITY_VLAN_ID_CAM) writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL | idx, &regs->CAMADDR); else writeb(CAMADDR_CAMEN | idx, &regs->CAMADDR); if (cam_type == VELOCITY_VLAN_ID_CAM) writew(*((u16 *) addr), &regs->MARCAM[0]); else { for (i = 0; i < 6; i++) { writeb(*addr++, &(regs->MARCAM[i])); } } BYTE_REG_BITS_ON(CAMCR_CAMWR, &regs->CAMCR); udelay(10); writeb(0, &regs->CAMADDR); /* Select mar */ BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR); } /** * mac_get_cam - fetch CAM data * @regs: register block of this velocity * @idx: Cam index * @addr: buffer to hold up to 6 bytes of CAM data * @cam_type: CAM to load * * Load an address or vlan tag from a CAM into the buffer provided by * the caller. VLAN tags are 2 bytes the address cam entries are 6. */ static inline void mac_get_cam(struct mac_regs __iomem * regs, int idx, u8 *addr, enum velocity_cam_type cam_type) { int i; /* Select CAM mask */ BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR); idx &= (64 - 1); if (cam_type == VELOCITY_VLAN_ID_CAM) writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL | idx, &regs->CAMADDR); else writeb(CAMADDR_CAMEN | idx, &regs->CAMADDR); BYTE_REG_BITS_ON(CAMCR_CAMRD, &regs->CAMCR); udelay(10); if (cam_type == VELOCITY_VLAN_ID_CAM) *((u16 *) addr) = readw(&(regs->MARCAM[0])); else for (i = 0; i < 6; i++, addr++) *((u8 *) addr) = readb(&(regs->MARCAM[i])); writeb(0, &regs->CAMADDR); /* Select mar */ BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR); } /** * mac_wol_reset - reset WOL after exiting low power * @regs: register block of this velocity * * Called after we drop out of wake on lan mode in order to * reset the Wake on lan features. This function doesn't restore * the rest of the logic from the result of sleep/wakeup */ static inline void mac_wol_reset(struct mac_regs __iomem * regs) { /* Turn off SWPTAG right after leaving power mode */ BYTE_REG_BITS_OFF(STICKHW_SWPTAG, &regs->STICKHW); /* clear sticky bits */ BYTE_REG_BITS_OFF((STICKHW_DS1 | STICKHW_DS0), &regs->STICKHW); BYTE_REG_BITS_OFF(CHIPGCR_FCGMII, &regs->CHIPGCR); BYTE_REG_BITS_OFF(CHIPGCR_FCMODE, &regs->CHIPGCR); /* disable force PME-enable */ writeb(WOLCFG_PMEOVR, &regs->WOLCFGClr); /* disable power-event config bit */ writew(0xFFFF, &regs->WOLCRClr); /* clear power status */ writew(0xFFFF, &regs->WOLSRClr); } /* * Header for WOL definitions. Used to compute hashes */ typedef u8 MCAM_ADDR[ETH_ALEN]; struct arp_packet { u8 dest_mac[ETH_ALEN]; u8 src_mac[ETH_ALEN]; u16 type; u16 ar_hrd; u16 ar_pro; u8 ar_hln; u8 ar_pln; u16 ar_op; u8 ar_sha[ETH_ALEN]; u8 ar_sip[4]; u8 ar_tha[ETH_ALEN]; u8 ar_tip[4]; } __attribute__ ((__packed__)); struct _magic_packet { u8 dest_mac[6]; u8 src_mac[6]; u16 type; u8 MAC[16][6]; u8 password[6]; } __attribute__ ((__packed__)); /* * Store for chip context when saving and restoring status. Not * all fields are saved/restored currently. */ struct velocity_context { u8 mac_reg[256]; MCAM_ADDR cam_addr[MCAM_SIZE]; u16 vcam[VCAM_SIZE]; u32 cammask[2]; u32 patcrc[2]; u32 pattern[8]; }; /* * MII registers. */ /* * Registers in the MII (offset unit is WORD) */ #define MII_REG_BMCR 0x00 // physical address #define MII_REG_BMSR 0x01 // #define MII_REG_PHYID1 0x02 // OUI #define MII_REG_PHYID2 0x03 // OUI + Module ID + REV ID #define MII_REG_ANAR 0x04 // #define MII_REG_ANLPAR 0x05 // #define MII_REG_G1000CR 0x09 // #define MII_REG_G1000SR 0x0A // #define MII_REG_MODCFG 0x10 // #define MII_REG_TCSR 0x16 // #define MII_REG_PLED 0x1B // // NS, MYSON only #define MII_REG_PCR 0x17 // // ESI only #define MII_REG_PCSR 0x17 // #define MII_REG_AUXCR 0x1C // // Marvell 88E1000/88E1000S #define MII_REG_PSCR 0x10 // PHY specific control register // // Bits in the BMCR register // #define BMCR_RESET 0x8000 // #define BMCR_LBK 0x4000 // #define BMCR_SPEED100 0x2000 // #define BMCR_AUTO 0x1000 // #define BMCR_PD 0x0800 // #define BMCR_ISO 0x0400 // #define BMCR_REAUTO 0x0200 // #define BMCR_FDX 0x0100 // #define BMCR_SPEED1G 0x0040 // // // Bits in the BMSR register // #define BMSR_AUTOCM 0x0020 // #define BMSR_LNK 0x0004 // // // Bits in the ANAR register // #define ANAR_ASMDIR 0x0800 // Asymmetric PAUSE support #define ANAR_PAUSE 0x0400 // Symmetric PAUSE Support #define ANAR_T4 0x0200 // #define ANAR_TXFD 0x0100 // #define ANAR_TX 0x0080 // #define ANAR_10FD 0x0040 // #define ANAR_10 0x0020 // // // Bits in the ANLPAR register // #define ANLPAR_ASMDIR 0x0800 // Asymmetric PAUSE support #define ANLPAR_PAUSE 0x0400 // Symmetric PAUSE Support #define ANLPAR_T4 0x0200 // #define ANLPAR_TXFD 0x0100 // #define ANLPAR_TX 0x0080 // #define ANLPAR_10FD 0x0040 // #define ANLPAR_10 0x0020 // // // Bits in the G1000CR register // #define G1000CR_1000FD 0x0200 // PHY is 1000-T Full-duplex capable #define G1000CR_1000 0x0100 // PHY is 1000-T Half-duplex capable // // Bits in the G1000SR register // #define G1000SR_1000FD 0x0800 // LP PHY is 1000-T Full-duplex capable #define G1000SR_1000 0x0400 // LP PHY is 1000-T Half-duplex capable #define TCSR_ECHODIS 0x2000 // #define AUXCR_MDPPS 0x0004 // // Bits in the PLED register #define PLED_LALBE 0x0004 // // Marvell 88E1000/88E1000S Bits in the PHY specific control register (10h) #define PSCR_ACRSTX 0x0800 // Assert CRS on Transmit #define PHYID_CICADA_CS8201 0x000FC410UL #define PHYID_VT3216_32BIT 0x000FC610UL #define PHYID_VT3216_64BIT 0x000FC600UL #define PHYID_MARVELL_1000 0x01410C50UL #define PHYID_MARVELL_1000S 0x01410C40UL #define PHYID_REV_ID_MASK 0x0000000FUL #define PHYID_GET_PHY_REV_ID(i) ((i) & PHYID_REV_ID_MASK) #define PHYID_GET_PHY_ID(i) ((i) & ~PHYID_REV_ID_MASK) #define MII_REG_BITS_ON(x,i,p) do {\ u16 w;\ velocity_mii_read((p),(i),&(w));\ (w)|=(x);\ velocity_mii_write((p),(i),(w));\ } while (0) #define MII_REG_BITS_OFF(x,i,p) do {\ u16 w;\ velocity_mii_read((p),(i),&(w));\ (w)&=(~(x));\ velocity_mii_write((p),(i),(w));\ } while (0) #define MII_REG_BITS_IS_ON(x,i,p) ({\ u16 w;\ velocity_mii_read((p),(i),&(w));\ ((int) ((w) & (x)));}) #define MII_GET_PHY_ID(p) ({\ u32 id;\ velocity_mii_read((p),MII_REG_PHYID2,(u16 *) &id);\ velocity_mii_read((p),MII_REG_PHYID1,((u16 *) &id)+1);\ (id);}) /* * Inline debug routine */ enum velocity_msg_level { MSG_LEVEL_ERR = 0, //Errors that will cause abnormal operation. MSG_LEVEL_NOTICE = 1, //Some errors need users to be notified. MSG_LEVEL_INFO = 2, //Normal message. MSG_LEVEL_VERBOSE = 3, //Will report all trival errors. MSG_LEVEL_DEBUG = 4 //Only for debug purpose. }; #ifdef VELOCITY_DEBUG #define ASSERT(x) { \ if (!(x)) { \ printk(KERN_ERR "assertion %s failed: file %s line %d\n", #x,\ __FUNCTION__, __LINE__);\ BUG(); \ }\ } #define VELOCITY_DBG(p,args...) printk(p, ##args) #else #define ASSERT(x) #define VELOCITY_DBG(x) #endif #define VELOCITY_PRT(l, p, args...) do {if (l<=msglevel) printk( p ,##args);} while (0) #define VELOCITY_PRT_CAMMASK(p,t) {\ int i;\ if ((t)==VELOCITY_MULTICAST_CAM) {\ for (i=0;i<(MCAM_SIZE/8);i++)\ printk("%02X",(p)->mCAMmask[i]);\ }\ else {\ for (i=0;i<(VCAM_SIZE/8);i++)\ printk("%02X",(p)->vCAMmask[i]);\ }\ printk("\n");\ } #define VELOCITY_WOL_MAGIC 0x00000000UL #define VELOCITY_WOL_PHY 0x00000001UL #define VELOCITY_WOL_ARP 0x00000002UL #define VELOCITY_WOL_UCAST 0x00000004UL #define VELOCITY_WOL_BCAST 0x00000010UL #define VELOCITY_WOL_MCAST 0x00000020UL #define VELOCITY_WOL_MAGIC_SEC 0x00000040UL /* * Flags for options */ #define VELOCITY_FLAGS_TAGGING 0x00000001UL #define VELOCITY_FLAGS_TX_CSUM 0x00000002UL #define VELOCITY_FLAGS_RX_CSUM 0x00000004UL #define VELOCITY_FLAGS_IP_ALIGN 0x00000008UL #define VELOCITY_FLAGS_VAL_PKT_LEN 0x00000010UL #define VELOCITY_FLAGS_FLOW_CTRL 0x01000000UL /* * Flags for driver status */ #define VELOCITY_FLAGS_OPENED 0x00010000UL #define VELOCITY_FLAGS_VMNS_CONNECTED 0x00020000UL #define VELOCITY_FLAGS_VMNS_COMMITTED 0x00040000UL #define VELOCITY_FLAGS_WOL_ENABLED 0x00080000UL /* * Flags for MII status */ #define VELOCITY_LINK_FAIL 0x00000001UL #define VELOCITY_SPEED_10 0x00000002UL #define VELOCITY_SPEED_100 0x00000004UL #define VELOCITY_SPEED_1000 0x00000008UL #define VELOCITY_DUPLEX_FULL 0x00000010UL #define VELOCITY_AUTONEG_ENABLE 0x00000020UL #define VELOCITY_FORCED_BY_EEPROM 0x00000040UL /* * For velocity_set_media_duplex */ #define VELOCITY_LINK_CHANGE 0x00000001UL enum speed_opt { SPD_DPX_AUTO = 0, SPD_DPX_100_HALF = 1, SPD_DPX_100_FULL = 2, SPD_DPX_10_HALF = 3, SPD_DPX_10_FULL = 4 }; enum velocity_init_type { VELOCITY_INIT_COLD = 0, VELOCITY_INIT_RESET, VELOCITY_INIT_WOL }; enum velocity_flow_cntl_type { FLOW_CNTL_DEFAULT = 1, FLOW_CNTL_TX, FLOW_CNTL_RX, FLOW_CNTL_TX_RX, FLOW_CNTL_DISABLE, }; struct velocity_opt { int numrx; /* Number of RX descriptors */ int numtx; /* Number of TX descriptors */ enum speed_opt spd_dpx; /* Media link mode */ int vid; /* vlan id */ int DMA_length; /* DMA length */ int rx_thresh; /* RX_THRESH */ int flow_cntl; int wol_opts; /* Wake on lan options */ int td_int_count; int int_works; int rx_bandwidth_hi; int rx_bandwidth_lo; int rx_bandwidth_en; u32 flags; }; struct velocity_info { struct list_head list; struct pci_dev *pdev; struct net_device *dev; struct net_device_stats stats; dma_addr_t rd_pool_dma; dma_addr_t td_pool_dma[TX_QUEUE_NO]; dma_addr_t tx_bufs_dma; u8 *tx_bufs; u8 ip_addr[4]; enum chip_type chip_id; struct mac_regs __iomem * mac_regs; unsigned long memaddr; unsigned long ioaddr; u32 io_size; u8 rev_id; #define AVAIL_TD(p,q) ((p)->options.numtx-((p)->td_used[(q)])) int num_txq; volatile int td_used[TX_QUEUE_NO]; int td_curr[TX_QUEUE_NO]; int td_tail[TX_QUEUE_NO]; struct tx_desc *td_rings[TX_QUEUE_NO]; struct velocity_td_info *td_infos[TX_QUEUE_NO]; int rd_curr; int rd_dirty; u32 rd_filled; struct rx_desc *rd_ring; struct velocity_rd_info *rd_info; /* It's an array */ #define GET_RD_BY_IDX(vptr, idx) (vptr->rd_ring[idx]) u32 mib_counter[MAX_HW_MIB_COUNTER]; struct velocity_opt options; u32 int_mask; u32 flags; int rx_buf_sz; u32 mii_status; u32 phy_id; int multicast_limit; u8 vCAMmask[(VCAM_SIZE / 8)]; u8 mCAMmask[(MCAM_SIZE / 8)]; spinlock_t lock; int wol_opts; u8 wol_passwd[6]; struct velocity_context context; u32 ticks; u32 rx_bytes; }; /** * velocity_get_ip - find an IP address for the device * @vptr: Velocity to query * * Dig out an IP address for this interface so that we can * configure wakeup with WOL for ARP. If there are multiple IP * addresses on this chain then we use the first - multi-IP WOL is not * supported. * * CHECK ME: locking */ static inline int velocity_get_ip(struct velocity_info *vptr) { struct in_device *in_dev = (struct in_device *) vptr->dev->ip_ptr; struct in_ifaddr *ifa; if (in_dev != NULL) { ifa = (struct in_ifaddr *) in_dev->ifa_list; if (ifa != NULL) { memcpy(vptr->ip_addr, &ifa->ifa_address, 4); return 0; } } return -ENOENT; } /** * velocity_update_hw_mibs - fetch MIB counters from chip * @vptr: velocity to update * * The velocity hardware keeps certain counters in the hardware * side. We need to read these when the user asks for statistics * or when they overflow (causing an interrupt). The read of the * statistic clears it, so we keep running master counters in user * space. */ static inline void velocity_update_hw_mibs(struct velocity_info *vptr) { u32 tmp; int i; BYTE_REG_BITS_ON(MIBCR_MIBFLSH, &(vptr->mac_regs->MIBCR)); while (BYTE_REG_BITS_IS_ON(MIBCR_MIBFLSH, &(vptr->mac_regs->MIBCR))); BYTE_REG_BITS_ON(MIBCR_MPTRINI, &(vptr->mac_regs->MIBCR)); for (i = 0; i < HW_MIB_SIZE; i++) { tmp = readl(&(vptr->mac_regs->MIBData)) & 0x00FFFFFFUL; vptr->mib_counter[i] += tmp; } } /** * init_flow_control_register - set up flow control * @vptr: velocity to configure * * Configure the flow control registers for this velocity device. */ static inline void init_flow_control_register(struct velocity_info *vptr) { struct mac_regs __iomem * regs = vptr->mac_regs; /* Set {XHITH1, XHITH0, XLTH1, XLTH0} in FlowCR1 to {1, 0, 1, 1} depend on RD=64, and Turn on XNOEN in FlowCR1 */ writel((CR0_XONEN | CR0_XHITH1 | CR0_XLTH1 | CR0_XLTH0), &regs->CR0Set); writel((CR0_FDXTFCEN | CR0_FDXRFCEN | CR0_HDXFCEN | CR0_XHITH0), &regs->CR0Clr); /* Set TxPauseTimer to 0xFFFF */ writew(0xFFFF, &regs->tx_pause_timer); /* Initialize RBRDU to Rx buffer count. */ writew(vptr->options.numrx, &regs->RBRDU); } #endif