if_sbni.c revision 86752
1/*
2 * Copyright (c) 1997-2001 Granch, Ltd. All rights reserved.
3 * Author: Denis I.Timofeev <timofeev@granch.ru>
4 *
5 * Redistributon and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice unmodified, this list of conditions, and the following
10 *    disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/sys/dev/sbni/if_sbni.c 86752 2001-11-21 22:29:35Z fjoe $
28 */
29
30/*
31 * Device driver for Granch SBNI12 leased line adapters
32 *
33 * Revision 2.0.0  1997/08/06
34 * Initial revision by Alexey Zverev
35 *
36 * Revision 2.0.1 1997/08/11
37 * Additional internal statistics support (tx statistics)
38 *
39 * Revision 2.0.2 1997/11/05
40 * if_bpf bug has been fixed
41 *
42 * Revision 2.0.3 1998/12/20
43 * Memory leakage has been eliminated in
44 * the sbni_st and sbni_timeout routines.
45 *
46 * Revision 3.0 2000/08/10 by Yaroslav Polyakov
47 * Support for PCI cards. 4.1 modification.
48 *
49 * Revision 3.1 2000/09/12
50 * Removed extra #defines around bpf functions
51 *
52 * Revision 4.0 2000/11/23 by Denis Timofeev
53 * Completely redesigned the buffer management
54 *
55 * Revision 4.1 2001/01/21
56 * Support for PCI Dual cards and new SBNI12D-10, -11 Dual/ISA cards
57 *
58 * Written with reference to NE2000 driver developed by David Greenman.
59 */
60
61
62#include <sys/param.h>
63#include <sys/systm.h>
64#include <sys/socket.h>
65#include <sys/sockio.h>
66#include <sys/mbuf.h>
67#include <sys/kernel.h>
68#include <sys/proc.h>
69#include <sys/callout.h>
70#include <sys/syslog.h>
71
72#include <net/if.h>
73#include <net/ethernet.h>
74#include <net/if_arp.h>
75#include <net/bpf.h>
76
77#include <dev/sbni/if_sbnireg.h>
78#include <dev/sbni/if_sbnivar.h>
79
80#define ASM_CRC 1
81
82static void	sbni_init(void *);
83static void	sbni_start(struct ifnet *);
84static int	sbni_ioctl(struct ifnet *, u_long, caddr_t);
85static void	sbni_watchdog(struct ifnet *);
86static void	sbni_stop(struct sbni_softc *);
87static void	handle_channel(struct sbni_softc *);
88
89static void	card_start(struct sbni_softc *);
90static int	recv_frame(struct sbni_softc *);
91static void	send_frame(struct sbni_softc *);
92static int	upload_data(struct sbni_softc *, u_int, u_int, u_int, u_int32_t);
93static int	skip_tail(struct sbni_softc *, u_int, u_int32_t);
94static void	interpret_ack(struct sbni_softc *, u_int);
95static void	download_data(struct sbni_softc *, u_int32_t *);
96static void	prepare_to_send(struct sbni_softc *);
97static void	drop_xmit_queue(struct sbni_softc *);
98static int	get_rx_buf(struct sbni_softc *);
99static void	indicate_pkt(struct sbni_softc *);
100static void	change_level(struct sbni_softc *);
101static int	check_fhdr(struct sbni_softc *, u_int *, u_int *,
102			   u_int *, u_int *, u_int32_t *);
103static int	append_frame_to_pkt(struct sbni_softc *, u_int, u_int32_t);
104static void	timeout_change_level(struct sbni_softc *);
105static void	send_frame_header(struct sbni_softc *, u_int32_t *);
106static void	set_initial_values(struct sbni_softc *, struct sbni_flags);
107
108static u_int32_t	calc_crc32(u_int32_t, caddr_t, u_int);
109static timeout_t	sbni_timeout;
110
111static __inline u_char	sbni_inb(struct sbni_softc *, enum sbni_reg);
112static __inline void	sbni_outb(struct sbni_softc *, enum sbni_reg, u_char);
113static __inline void	sbni_insb(struct sbni_softc *, u_char *, u_int);
114static __inline void	sbni_outsb(struct sbni_softc *, u_char *, u_int);
115
116static u_int32_t crc32tab[];
117
118#ifdef SBNI_DUAL_COMPOUND
119struct sbni_softc *headlist;
120#endif
121
122u_int32_t next_sbni_unit;
123
124/* -------------------------------------------------------------------------- */
125
126static __inline u_char
127sbni_inb(struct sbni_softc *sc, enum sbni_reg reg)
128{
129	return (inb(sc->base_addr + reg));
130}
131
132static __inline void
133sbni_outb(struct sbni_softc *sc, enum sbni_reg reg, u_char value)
134{
135	outb(sc->base_addr + reg, value);
136}
137
138static __inline void
139sbni_insb(struct sbni_softc *sc, u_char *to, u_int len)
140{
141	insb(sc->base_addr + DAT, to, len);
142}
143
144static __inline void
145sbni_outsb(struct sbni_softc *sc, u_char *from, u_int len)
146{
147	outsb(sc->base_addr + DAT, from, len);
148}
149
150
151/*
152	Valid combinations in CSR0 (for probing):
153
154	VALID_DECODER	0000,0011,1011,1010
155
156				    	; 0   ; -
157				TR_REQ	; 1   ; +
158			TR_RDY	    	; 2   ; -
159			TR_RDY	TR_REQ	; 3   ; +
160		BU_EMP		    	; 4   ; +
161		BU_EMP	     	TR_REQ	; 5   ; +
162		BU_EMP	TR_RDY	    	; 6   ; -
163		BU_EMP	TR_RDY	TR_REQ	; 7   ; +
164	RC_RDY 		     		; 8   ; +
165	RC_RDY			TR_REQ	; 9   ; +
166	RC_RDY		TR_RDY		; 10  ; -
167	RC_RDY		TR_RDY	TR_REQ	; 11  ; -
168	RC_RDY	BU_EMP			; 12  ; -
169	RC_RDY	BU_EMP		TR_REQ	; 13  ; -
170	RC_RDY	BU_EMP	TR_RDY		; 14  ; -
171	RC_RDY	BU_EMP	TR_RDY	TR_REQ	; 15  ; -
172*/
173
174#define VALID_DECODER	(2 + 8 + 0x10 + 0x20 + 0x80 + 0x100 + 0x200)
175
176
177int
178sbni_probe(struct sbni_softc *sc)
179{
180	u_char csr0;
181
182	csr0 = sbni_inb(sc, CSR0);
183	if (csr0 != 0xff && csr0 != 0x00) {
184		csr0 &= ~EN_INT;
185		if (csr0 & BU_EMP)
186			csr0 |= EN_INT;
187
188		if (VALID_DECODER & (1 << (csr0 >> 4)))
189			return (0);
190	}
191
192	return (ENXIO);
193}
194
195
196/*
197 * Install interface into kernel networking data structures
198 */
199void
200sbni_attach(struct sbni_softc *sc, int unit, struct sbni_flags flags)
201{
202	struct ifnet *ifp;
203	u_char csr0;
204
205	ifp = &sc->arpcom.ac_if;
206	sbni_outb(sc, CSR0, 0);
207	set_initial_values(sc, flags);
208
209	callout_handle_init(&sc->wch);
210	if (!ifp->if_name) {
211		/* Initialize ifnet structure */
212		ifp->if_softc	= sc;
213		ifp->if_unit	= unit;
214		ifp->if_name	= "sbni";
215		ifp->if_init	= sbni_init;
216		ifp->if_start	= sbni_start;
217	        ifp->if_output	= ether_output;
218		ifp->if_ioctl	= sbni_ioctl;
219		ifp->if_watchdog	= sbni_watchdog;
220		ifp->if_snd.ifq_maxlen	= IFQ_MAXLEN;
221
222		/* report real baud rate */
223		csr0 = sbni_inb(sc, CSR0);
224		ifp->if_baudrate =
225			(csr0 & 0x01 ? 500000 : 2000000) / (1 << flags.rate);
226
227	        ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
228		ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
229	}
230	/* device attach does transition from UNCONFIGURED to IDLE state */
231
232	printf("%s%d: speed %ld, address %6D, rxl ", ifp->if_name,
233	       ifp->if_unit, ifp->if_baudrate, sc->arpcom.ac_enaddr, ":");
234	if (sc->delta_rxl)
235		printf("auto\n");
236	else
237		printf("%d (fixed)\n", sc->cur_rxl_index);
238}
239
240/* -------------------------------------------------------------------------- */
241
242static void
243sbni_init(void *xsc)
244{
245	struct sbni_softc *sc;
246	struct ifnet *ifp;
247	int  s;
248
249	sc = (struct sbni_softc *)xsc;
250	ifp = &sc->arpcom.ac_if;
251
252	/* address not known */
253	if (TAILQ_EMPTY(&ifp->if_addrhead))
254		return;
255
256	/*
257	 * kludge to avoid multiple initialization when more than once
258	 * protocols configured
259	 */
260	if (ifp->if_flags & IFF_RUNNING)
261		return;
262
263	s = splimp();
264	ifp->if_timer = 0;
265	card_start(sc);
266	sc->wch = timeout(sbni_timeout, sc, hz/SBNI_HZ);
267
268	ifp->if_flags |= IFF_RUNNING;
269	ifp->if_flags &= ~IFF_OACTIVE;
270
271	/* attempt to start output */
272	sbni_start(ifp);
273	splx(s);
274}
275
276
277static void
278sbni_start(struct ifnet *ifp)
279{
280	struct sbni_softc *sc = ifp->if_softc;
281	if (sc->tx_frameno == 0)
282		prepare_to_send(sc);
283}
284
285
286static void
287sbni_stop(struct sbni_softc *sc)
288{
289	sbni_outb(sc, CSR0, 0);
290	drop_xmit_queue(sc);
291
292	if (sc->rx_buf_p) {
293		m_freem(sc->rx_buf_p);
294		sc->rx_buf_p = NULL;
295	}
296
297	untimeout(sbni_timeout, sc, sc->wch);
298	sc->wch.callout = NULL;
299}
300
301/* -------------------------------------------------------------------------- */
302
303/* interrupt handler */
304
305/*
306 * 	SBNI12D-10, -11/ISA boards within "common interrupt" mode could not
307 * be looked as two independent single-channel devices. Every channel seems
308 * as Ethernet interface but interrupt handler must be common. Really, first
309 * channel ("master") driver only registers the handler. In it's struct softc
310 * it has got pointer to "slave" channel's struct softc and handles that's
311 * interrupts too.
312 *	softc of successfully attached ISA SBNI boards is linked to list.
313 * While next board driver is initialized, it scans this list. If one
314 * has found softc with same irq and ioaddr different by 4 then it assumes
315 * this board to be "master".
316 */
317
318void
319sbni_intr(void *arg)
320{
321	struct sbni_softc *sc;
322	int repeat;
323
324	sc = (struct sbni_softc *)arg;
325
326	do {
327		repeat = 0;
328		if (sbni_inb(sc, CSR0) & (RC_RDY | TR_RDY)) {
329			handle_channel(sc);
330			repeat = 1;
331		}
332		if (sc->slave_sc 	/* second channel present */
333		    && (sbni_inb(sc->slave_sc, CSR0) & (RC_RDY | TR_RDY))) {
334			handle_channel(sc->slave_sc);
335			repeat = 1;
336		}
337	} while (repeat);
338}
339
340
341static void
342handle_channel(struct sbni_softc *sc)
343{
344	int req_ans;
345	u_char csr0;
346
347	sbni_outb(sc, CSR0, (sbni_inb(sc, CSR0) & ~EN_INT) | TR_REQ);
348
349	sc->timer_ticks = CHANGE_LEVEL_START_TICKS;
350	for (;;) {
351		csr0 = sbni_inb(sc, CSR0);
352		if ((csr0 & (RC_RDY | TR_RDY)) == 0)
353			break;
354
355		req_ans = !(sc->state & FL_PREV_OK);
356
357		if (csr0 & RC_RDY)
358			req_ans = recv_frame(sc);
359
360		/*
361		 * TR_RDY always equals 1 here because we have owned the marker,
362		 * and we set TR_REQ when disabled interrupts
363		 */
364		csr0 = sbni_inb(sc, CSR0);
365		if ((csr0 & TR_RDY) == 0 || (csr0 & RC_RDY) != 0)
366			printf("sbni: internal error!\n");
367
368		/* if state & FL_NEED_RESEND != 0 then tx_frameno != 0 */
369		if (req_ans || sc->tx_frameno != 0)
370			send_frame(sc);
371		else
372			/* send the marker without any data */
373			sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) & ~TR_REQ);
374	}
375
376	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) | EN_INT);
377}
378
379
380/*
381 * Routine returns 1 if it need to acknoweledge received frame.
382 * Empty frame received without errors won't be acknoweledged.
383 */
384
385static int
386recv_frame(struct sbni_softc *sc)
387{
388	u_int32_t crc;
389	u_int framelen, frameno, ack;
390	u_int is_first, frame_ok;
391
392	crc = CRC32_INITIAL;
393	if (check_fhdr(sc, &framelen, &frameno, &ack, &is_first, &crc)) {
394		frame_ok = framelen > 4
395			   ? upload_data(sc, framelen, frameno, is_first, crc)
396			   : skip_tail(sc, framelen, crc);
397		if (frame_ok)
398			interpret_ack(sc, ack);
399	} else
400		frame_ok = 0;
401
402	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) ^ CT_ZER);
403	if (frame_ok) {
404		sc->state |= FL_PREV_OK;
405		if (framelen > 4)
406			sc->in_stats.all_rx_number++;
407	} else {
408		sc->state &= ~FL_PREV_OK;
409		change_level(sc);
410		sc->in_stats.all_rx_number++;
411		sc->in_stats.bad_rx_number++;
412	}
413
414	return (!frame_ok || framelen > 4);
415}
416
417
418static void
419send_frame(struct sbni_softc *sc)
420{
421	u_int32_t crc;
422	u_char csr0;
423
424	crc = CRC32_INITIAL;
425	if (sc->state & FL_NEED_RESEND) {
426
427		/* if frame was sended but not ACK'ed - resend it */
428		if (sc->trans_errors) {
429			sc->trans_errors--;
430			if (sc->framelen != 0)
431				sc->in_stats.resend_tx_number++;
432		} else {
433			/* cannot xmit with many attempts */
434			drop_xmit_queue(sc);
435			goto do_send;
436		}
437	} else
438		sc->trans_errors = TR_ERROR_COUNT;
439
440	send_frame_header(sc, &crc);
441	sc->state |= FL_NEED_RESEND;
442	/*
443	 * FL_NEED_RESEND will be cleared after ACK, but if empty
444	 * frame sended then in prepare_to_send next frame
445	 */
446
447
448	if (sc->framelen) {
449		download_data(sc, &crc);
450		sc->in_stats.all_tx_number++;
451		sc->state |= FL_WAIT_ACK;
452	}
453
454	sbni_outsb(sc, (u_char *)&crc, sizeof crc);
455
456do_send:
457	csr0 = sbni_inb(sc, CSR0);
458	sbni_outb(sc, CSR0, csr0 & ~TR_REQ);
459
460	if (sc->tx_frameno) {
461		/* next frame exists - request to send */
462		sbni_outb(sc, CSR0, csr0 | TR_REQ);
463	}
464}
465
466
467static void
468download_data(struct sbni_softc *sc, u_int32_t *crc_p)
469{
470	struct mbuf *m;
471	caddr_t	data_p;
472	u_int data_len, pos, slice;
473
474	data_p = NULL;		/* initialized to avoid warn */
475	pos = 0;
476
477	for (m = sc->tx_buf_p;  m != NULL && pos < sc->pktlen;  m = m->m_next) {
478		if (pos + m->m_len > sc->outpos) {
479			data_len = m->m_len - (sc->outpos - pos);
480			data_p = mtod(m, caddr_t) + (sc->outpos - pos);
481
482			goto do_copy;
483		} else
484			pos += m->m_len;
485	}
486
487	data_len = 0;
488
489do_copy:
490	pos = 0;
491	do {
492		if (data_len) {
493			slice = min(data_len, sc->framelen - pos);
494			sbni_outsb(sc, data_p, slice);
495			*crc_p = calc_crc32(*crc_p, data_p, slice);
496
497			pos += slice;
498			if (data_len -= slice)
499				data_p += slice;
500			else {
501				do	m = m->m_next;
502				while (m != NULL && m->m_len == 0);
503
504				if (m) {
505					data_len = m->m_len;
506					data_p = mtod(m, caddr_t);
507				}
508			}
509		} else {
510			/* frame too short - zero padding */
511
512			pos = sc->framelen - pos;
513			while (pos--) {
514				sbni_outb(sc, DAT, 0);
515				*crc_p = CRC32(0, *crc_p);
516			}
517			return;
518		}
519	} while (pos < sc->framelen);
520}
521
522
523static int
524upload_data(struct sbni_softc *sc, u_int framelen, u_int frameno,
525	    u_int is_first, u_int32_t crc)
526{
527	int frame_ok;
528
529	if (is_first) {
530		sc->wait_frameno = frameno;
531		sc->inppos = 0;
532	}
533
534	if (sc->wait_frameno == frameno) {
535
536		if (sc->inppos + framelen  <=  ETHER_MAX_LEN) {
537			frame_ok = append_frame_to_pkt(sc, framelen, crc);
538
539		/*
540		 * if CRC is right but framelen incorrect then transmitter
541		 * error was occured... drop entire packet
542		 */
543		} else if ((frame_ok = skip_tail(sc, framelen, crc)) != 0) {
544			sc->wait_frameno = 0;
545			sc->inppos = 0;
546			sc->arpcom.ac_if.if_ierrors++;
547			/* now skip all frames until is_first != 0 */
548		}
549	} else
550		frame_ok = skip_tail(sc, framelen, crc);
551
552	if (is_first && !frame_ok) {
553		/*
554		 * Frame has been violated, but we have stored
555		 * is_first already... Drop entire packet.
556		 */
557		sc->wait_frameno = 0;
558		sc->arpcom.ac_if.if_ierrors++;
559	}
560
561	return (frame_ok);
562}
563
564
565static __inline void	send_complete(struct sbni_softc *);
566
567static __inline void
568send_complete(struct sbni_softc *sc)
569{
570	m_freem(sc->tx_buf_p);
571	sc->tx_buf_p = NULL;
572	sc->arpcom.ac_if.if_opackets++;
573}
574
575
576static void
577interpret_ack(struct sbni_softc *sc, u_int ack)
578{
579	if (ack == FRAME_SENT_OK) {
580		sc->state &= ~FL_NEED_RESEND;
581
582		if (sc->state & FL_WAIT_ACK) {
583			sc->outpos += sc->framelen;
584
585			if (--sc->tx_frameno)
586				sc->framelen = min(sc->maxframe,
587						   sc->pktlen - sc->outpos);
588			else {
589				send_complete(sc);
590				prepare_to_send(sc);
591			}
592		}
593	}
594
595	sc->state &= ~FL_WAIT_ACK;
596}
597
598
599/*
600 * Glue received frame with previous fragments of packet.
601 * Indicate packet when last frame would be accepted.
602 */
603
604static int
605append_frame_to_pkt(struct sbni_softc *sc, u_int framelen, u_int32_t crc)
606{
607	caddr_t p;
608
609	if (sc->inppos + framelen > ETHER_MAX_LEN)
610		return (0);
611
612	if (!sc->rx_buf_p && !get_rx_buf(sc))
613		return (0);
614
615	p = sc->rx_buf_p->m_data + sc->inppos;
616	sbni_insb(sc, p, framelen);
617	if (calc_crc32(crc, p, framelen) != CRC32_REMAINDER)
618		return (0);
619
620	sc->inppos += framelen - 4;
621	if (--sc->wait_frameno == 0) {		/* last frame received */
622		indicate_pkt(sc);
623		sc->arpcom.ac_if.if_ipackets++;
624	}
625
626	return (1);
627}
628
629
630/*
631 * Prepare to start output on adapter. Current priority must be set to splimp
632 * before this routine is called.
633 * Transmitter will be actually activated when marker has been accepted.
634 */
635
636static void
637prepare_to_send(struct sbni_softc *sc)
638{
639	struct mbuf *m;
640	u_int len;
641
642	/* sc->tx_buf_p == NULL here! */
643	if (sc->tx_buf_p)
644		printf("sbni: memory leak!\n");
645
646	sc->outpos = 0;
647	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
648
649	for (;;) {
650		IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, sc->tx_buf_p);
651		if (!sc->tx_buf_p) {
652			/* nothing to transmit... */
653			sc->pktlen     = 0;
654			sc->tx_frameno = 0;
655			sc->framelen   = 0;
656			sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
657			return;
658		}
659
660		for (len = 0, m = sc->tx_buf_p;  m;  m = m->m_next)
661			len += m->m_len;
662
663		if (len != 0)
664			break;
665		m_freem(sc->tx_buf_p);
666	}
667
668	if (len < SBNI_MIN_LEN)
669		len = SBNI_MIN_LEN;
670
671	sc->pktlen	= len;
672	sc->tx_frameno	= (len + sc->maxframe - 1) / sc->maxframe;
673	sc->framelen	= min(len, sc->maxframe);
674
675	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) | TR_REQ);
676	sc->arpcom.ac_if.if_flags |= IFF_OACTIVE;
677	if (sc->arpcom.ac_if.if_bpf)
678		bpf_mtap(&sc->arpcom.ac_if, sc->tx_buf_p);
679}
680
681
682static void
683drop_xmit_queue(struct sbni_softc *sc)
684{
685	struct mbuf *m;
686
687	if (sc->tx_buf_p) {
688		m_freem(sc->tx_buf_p);
689		sc->tx_buf_p = NULL;
690		sc->arpcom.ac_if.if_oerrors++;
691	}
692
693	for (;;) {
694		IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m);
695		if (m == NULL)
696			break;
697		m_freem(m);
698		sc->arpcom.ac_if.if_oerrors++;
699	}
700
701	sc->tx_frameno	= 0;
702	sc->framelen	= 0;
703	sc->outpos	= 0;
704	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
705	sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
706}
707
708
709static void
710send_frame_header(struct sbni_softc *sc, u_int32_t *crc_p)
711{
712	u_int32_t crc;
713	u_int len_field;
714	u_char value;
715
716	crc = *crc_p;
717	len_field = sc->framelen + 6;	/* CRC + frameno + reserved */
718
719	if (sc->state & FL_NEED_RESEND)
720		len_field |= FRAME_RETRY;	/* non-first attempt... */
721
722	if (sc->outpos == 0)
723		len_field |= FRAME_FIRST;
724
725	len_field |= (sc->state & FL_PREV_OK) ? FRAME_SENT_OK : FRAME_SENT_BAD;
726	sbni_outb(sc, DAT, SBNI_SIG);
727
728	value = (u_char)len_field;
729	sbni_outb(sc, DAT, value);
730	crc = CRC32(value, crc);
731	value = (u_char)(len_field >> 8);
732	sbni_outb(sc, DAT, value);
733	crc = CRC32(value, crc);
734
735	sbni_outb(sc, DAT, sc->tx_frameno);
736	crc = CRC32(sc->tx_frameno, crc);
737	sbni_outb(sc, DAT, 0);
738	crc = CRC32(0, crc);
739	*crc_p = crc;
740}
741
742
743/*
744 * if frame tail not needed (incorrect number or received twice),
745 * it won't store, but CRC will be calculated
746 */
747
748static int
749skip_tail(struct sbni_softc *sc, u_int tail_len, u_int32_t crc)
750{
751	while (tail_len--)
752		crc = CRC32(sbni_inb(sc, DAT), crc);
753
754	return (crc == CRC32_REMAINDER);
755}
756
757
758static int
759check_fhdr(struct sbni_softc *sc, u_int *framelen, u_int *frameno,
760	   u_int *ack, u_int *is_first, u_int32_t *crc_p)
761{
762	u_int32_t crc;
763	u_char value;
764
765	crc = *crc_p;
766	if (sbni_inb(sc, DAT) != SBNI_SIG)
767		return (0);
768
769	value = sbni_inb(sc, DAT);
770	*framelen = (u_int)value;
771	crc = CRC32(value, crc);
772	value = sbni_inb(sc, DAT);
773	*framelen |= ((u_int)value) << 8;
774	crc = CRC32(value, crc);
775
776	*ack = *framelen & FRAME_ACK_MASK;
777	*is_first = (*framelen & FRAME_FIRST) != 0;
778
779	if ((*framelen &= FRAME_LEN_MASK) < 6 || *framelen > SBNI_MAX_FRAME - 3)
780		return (0);
781
782	value = sbni_inb(sc, DAT);
783	*frameno = (u_int)value;
784	crc = CRC32(value, crc);
785
786	crc = CRC32(sbni_inb(sc, DAT), crc);		/* reserved byte */
787	*framelen -= 2;
788
789	*crc_p = crc;
790	return (1);
791}
792
793
794static int
795get_rx_buf(struct sbni_softc *sc)
796{
797	struct mbuf *m;
798
799	MGETHDR(m, M_DONTWAIT, MT_DATA);
800	if (m == NULL) {
801		printf("sbni%d: cannot allocate header mbuf\n",
802		       sc->arpcom.ac_if.if_unit);
803		return (0);
804	}
805
806	/*
807	 * We always put the received packet in a single buffer -
808	 * either with just an mbuf header or in a cluster attached
809	 * to the header. The +2 is to compensate for the alignment
810	 * fixup below.
811	 */
812	if (ETHER_MAX_LEN + 2 > MHLEN) {
813		/* Attach an mbuf cluster */
814		MCLGET(m, M_DONTWAIT);
815		if ((m->m_flags & M_EXT) == 0) {
816			m_freem(m);
817			return (0);
818		}
819	}
820	m->m_pkthdr.len = m->m_len = ETHER_MAX_LEN + 2;
821
822	/*
823	 * The +2 is to longword align the start of the real packet.
824	 * (sizeof ether_header == 14)
825	 * This is important for NFS.
826	 */
827	m_adj(m, 2);
828	sc->rx_buf_p = m;
829	return (1);
830}
831
832
833static void
834indicate_pkt(struct sbni_softc *sc)
835{
836	struct mbuf *m;
837	struct ether_header *eh;
838
839	m = sc->rx_buf_p;
840	m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
841	m->m_pkthdr.len   = m->m_len = sc->inppos;
842	eh = mtod(m, struct ether_header *);
843
844	/* Remove link layer address and indicate packet */
845	m_adj(m, sizeof(struct ether_header));
846	ether_input(&sc->arpcom.ac_if, eh, m);
847	sc->rx_buf_p = NULL;
848}
849
850/* -------------------------------------------------------------------------- */
851
852/*
853 * Routine checks periodically wire activity and regenerates marker if
854 * connect was inactive for a long time.
855 */
856
857static void
858sbni_timeout(void *xsc)
859{
860	struct sbni_softc *sc;
861	int s;
862	u_char csr0;
863
864	sc = (struct sbni_softc *)xsc;
865	s = splimp();
866
867	csr0 = sbni_inb(sc, CSR0);
868	if (csr0 & RC_CHK) {
869
870		if (sc->timer_ticks) {
871			if (csr0 & (RC_RDY | BU_EMP))
872				/* receiving not active */
873				sc->timer_ticks--;
874		} else {
875			sc->in_stats.timeout_number++;
876			if (sc->delta_rxl)
877				timeout_change_level(sc);
878
879			sbni_outb(sc, CSR1, *(u_char *)&sc->csr1 | PR_RES);
880			csr0 = sbni_inb(sc, CSR0);
881		}
882	}
883
884	sbni_outb(sc, CSR0, csr0 | RC_CHK);
885	sc->wch = timeout(sbni_timeout, sc, hz/SBNI_HZ);
886	splx(s);
887}
888
889/* -------------------------------------------------------------------------- */
890
891static void
892card_start(struct sbni_softc *sc)
893{
894	sc->timer_ticks = CHANGE_LEVEL_START_TICKS;
895	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
896	sc->state |= FL_PREV_OK;
897
898	sc->inppos = 0;
899	sc->wait_frameno = 0;
900
901	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1 | PR_RES);
902	sbni_outb(sc, CSR0, EN_INT);
903}
904
905/* -------------------------------------------------------------------------- */
906
907/*
908 * Device timeout/watchdog routine. Entered if the device neglects to
909 *	generate an interrupt after a transmit has been started on it.
910 */
911
912static void
913sbni_watchdog(struct ifnet *ifp)
914{
915	log(LOG_ERR, "sbni%d: device timeout\n", ifp->if_unit);
916	ifp->if_oerrors++;
917}
918
919
920static u_char rxl_tab[] = {
921	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08,
922	0x0a, 0x0c, 0x0f, 0x16, 0x18, 0x1a, 0x1c, 0x1f
923};
924
925#define SIZE_OF_TIMEOUT_RXL_TAB 4
926static u_char timeout_rxl_tab[] = {
927	0x03, 0x05, 0x08, 0x0b
928};
929
930static void
931set_initial_values(struct sbni_softc *sc, struct sbni_flags flags)
932{
933	if (flags.fixed_rxl) {
934		sc->delta_rxl = 0; /* disable receive level autodetection */
935		sc->cur_rxl_index = flags.rxl;
936	} else {
937		sc->delta_rxl = DEF_RXL_DELTA;
938		sc->cur_rxl_index = DEF_RXL;
939	}
940
941	sc->csr1.rate = flags.fixed_rate ? flags.rate : DEFAULT_RATE;
942	sc->csr1.rxl  = rxl_tab[sc->cur_rxl_index];
943	sc->maxframe  = DEFAULT_FRAME_LEN;
944
945	/*
946	 * generate Ethernet address (0x00ff01xxxxxx)
947	 */
948	*(u_int16_t*)sc->arpcom.ac_enaddr = htons(0x00ff);
949	if (flags.mac_addr)
950		*(u_int32_t*)(sc->arpcom.ac_enaddr+2) =
951			htonl(flags.mac_addr | 0x01000000);
952	else {
953		/* reading timer value */
954		outb(0x43, 0);
955		insb(0x40, sc->arpcom.ac_enaddr + 3, 4);
956		*(u_char*)(sc->arpcom.ac_enaddr + 2) = 0x01;
957	}
958}
959
960
961#ifdef SBNI_DUAL_COMPOUND
962
963#ifndef offsetof
964#define offsetof(type, member)		((u_int32_t)(&((type *)0)->member))
965#endif
966
967
968struct sbni_softc *
969connect_to_master(struct sbni_softc *sc)
970{
971	struct sbni_softc *p;
972
973	p = (struct sbni_softc *)(((char *)&headlist)
974	    - offsetof(struct sbni_softc, link));
975
976	for (; p->link; p = p->link) {
977		if (p->link->irq == sc->irq
978		    && (p->link->base_addr == sc->base_addr + 4
979			|| p->link->base_addr == sc->base_addr - 4)) {
980
981			struct sbni_softc  *t = p->link;
982			t->slave_sc = sc;
983			p->link = p->link->link;
984			return (t);
985		}
986	}
987
988	return (NULL);
989}
990
991#endif	/* SBNI_DUAL_COMPOUND */
992
993
994/* Receive level auto-selection */
995
996static void
997change_level(struct sbni_softc *sc)
998{
999	if (sc->delta_rxl == 0)		/* do not auto-negotiate RxL */
1000		return;
1001
1002	if (sc->cur_rxl_index == 0)
1003		sc->delta_rxl = 1;
1004	else if (sc->cur_rxl_index == 15)
1005		sc->delta_rxl = -1;
1006	else if (sc->cur_rxl_rcvd < sc->prev_rxl_rcvd)
1007		sc->delta_rxl = -sc->delta_rxl;
1008
1009	sc->csr1.rxl = rxl_tab[sc->cur_rxl_index += sc->delta_rxl];
1010	sbni_inb(sc, CSR0);	/* it needed for PCI cards */
1011	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1);
1012
1013	sc->prev_rxl_rcvd = sc->cur_rxl_rcvd;
1014	sc->cur_rxl_rcvd  = 0;
1015}
1016
1017
1018static void
1019timeout_change_level(struct sbni_softc *sc)
1020{
1021	sc->cur_rxl_index = timeout_rxl_tab[sc->timeout_rxl];
1022	if (++sc->timeout_rxl >= 4)
1023		sc->timeout_rxl = 0;
1024
1025	sc->csr1.rxl = rxl_tab[sc->cur_rxl_index];
1026	sbni_inb(sc, CSR0);
1027	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1);
1028
1029	sc->prev_rxl_rcvd = sc->cur_rxl_rcvd;
1030	sc->cur_rxl_rcvd  = 0;
1031}
1032
1033/* -------------------------------------------------------------------------- */
1034
1035/*
1036 * Process an ioctl request. This code needs some work - it looks
1037 *	pretty ugly.
1038 */
1039
1040static int
1041sbni_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
1042{
1043	struct sbni_softc *sc;
1044	struct ifreq *ifr;
1045	struct proc *p;
1046	struct sbni_in_stats *in_stats;
1047	struct sbni_flags flags;
1048	int error, s;
1049
1050	sc = ifp->if_softc;
1051	ifr = (struct ifreq *)data;
1052	p = curproc;
1053	error = 0;
1054
1055	s = splimp();
1056
1057	switch (command) {
1058	case SIOCSIFADDR:
1059	case SIOCGIFADDR:
1060			ether_ioctl(ifp, command, data);
1061			break;
1062
1063	case SIOCSIFFLAGS:
1064		/*
1065		 * If the interface is marked up and stopped, then start it.
1066		 * If it is marked down and running, then stop it.
1067		 */
1068		if (ifp->if_flags & IFF_UP) {
1069			if (!(ifp->if_flags & IFF_RUNNING))
1070				sbni_init(sc);
1071		} else {
1072			if (ifp->if_flags & IFF_RUNNING) {
1073				sbni_stop(sc);
1074				ifp->if_flags &= ~IFF_RUNNING;
1075			}
1076		}
1077		break;
1078
1079	case SIOCADDMULTI:
1080	case SIOCDELMULTI:
1081		/*
1082		 * Multicast list has changed; set the hardware filter
1083		 * accordingly.
1084		 */
1085		error = 0;
1086		/* if (ifr == NULL)
1087			error = EAFNOSUPPORT; */
1088		break;
1089
1090	case SIOCSIFMTU:
1091		if (ifr->ifr_mtu > ETHERMTU)
1092			error = EINVAL;
1093		else
1094			ifp->if_mtu = ifr->ifr_mtu;
1095		break;
1096
1097		/*
1098		 * SBNI specific ioctl
1099		 */
1100	case SIOCGHWFLAGS:	/* get flags */
1101		bcopy((caddr_t) sc->arpcom.ac_enaddr+3, (caddr_t) &flags, 3);
1102		flags.rxl = sc->cur_rxl_index;
1103		flags.rate = sc->csr1.rate;
1104		flags.fixed_rxl = (sc->delta_rxl == 0);
1105		flags.fixed_rate = 1;
1106		ifr->ifr_data = *(caddr_t*) &flags;
1107		break;
1108
1109	case SIOCGINSTATS:
1110		in_stats = (struct sbni_in_stats *)ifr->ifr_data;
1111		bcopy((void *)(&(sc->in_stats)), (void *)in_stats,
1112		      sizeof(struct sbni_in_stats));
1113		break;
1114
1115	case SIOCSHWFLAGS:	/* set flags */
1116		/* root only */
1117		error = suser(p);
1118		if (error)
1119			break;
1120		flags = *(struct sbni_flags*)&ifr->ifr_data;
1121		if (flags.fixed_rxl) {
1122			sc->delta_rxl = 0;
1123			sc->cur_rxl_index = flags.rxl;
1124		} else {
1125			sc->delta_rxl = DEF_RXL_DELTA;
1126			sc->cur_rxl_index = DEF_RXL;
1127		}
1128		sc->csr1.rxl = rxl_tab[sc->cur_rxl_index];
1129		sc->csr1.rate = flags.fixed_rate ? flags.rate : DEFAULT_RATE;
1130		if (flags.mac_addr)
1131			bcopy((caddr_t) &flags,
1132			      (caddr_t) sc->arpcom.ac_enaddr+3, 3);
1133
1134		/* Don't be afraid... */
1135		sbni_outb(sc, CSR1, *(char*)(&sc->csr1) | PR_RES);
1136		break;
1137
1138	case SIOCRINSTATS:
1139		if (!(error = suser(p)))	/* root only */
1140			bzero(&sc->in_stats, sizeof(struct sbni_in_stats));
1141		break;
1142
1143	default:
1144		error = EINVAL;
1145	}
1146
1147	splx(s);
1148	return (error);
1149}
1150
1151/* -------------------------------------------------------------------------- */
1152
1153#ifdef ASM_CRC
1154
1155static u_int32_t
1156calc_crc32(u_int32_t crc, caddr_t p, u_int len)
1157{
1158	register u_int32_t  _crc __asm ("ax");
1159	_crc = crc;
1160
1161	__asm __volatile (
1162		"xorl	%%ebx, %%ebx\n"
1163		"movl	%1, %%esi\n"
1164		"movl	%2, %%ecx\n"
1165		"movl	$crc32tab, %%edi\n"
1166		"shrl	$2, %%ecx\n"
1167		"jz	1f\n"
1168
1169		".align 4\n"
1170	"0:\n"
1171		"movb	%%al, %%bl\n"
1172		"movl	(%%esi), %%edx\n"
1173		"shrl	$8, %%eax\n"
1174		"xorb	%%dl, %%bl\n"
1175		"shrl	$8, %%edx\n"
1176		"xorl	(%%edi,%%ebx,4), %%eax\n"
1177
1178		"movb	%%al, %%bl\n"
1179		"shrl	$8, %%eax\n"
1180		"xorb	%%dl, %%bl\n"
1181		"shrl	$8, %%edx\n"
1182		"xorl	(%%edi,%%ebx,4), %%eax\n"
1183
1184		"movb	%%al, %%bl\n"
1185		"shrl	$8, %%eax\n"
1186		"xorb	%%dl, %%bl\n"
1187		"movb	%%dh, %%dl\n"
1188		"xorl	(%%edi,%%ebx,4), %%eax\n"
1189
1190		"movb	%%al, %%bl\n"
1191		"shrl	$8, %%eax\n"
1192		"xorb	%%dl, %%bl\n"
1193		"addl	$4, %%esi\n"
1194		"xorl	(%%edi,%%ebx,4), %%eax\n"
1195
1196		"decl	%%ecx\n"
1197		"jnz	0b\n"
1198
1199	"1:\n"
1200		"movl	%2, %%ecx\n"
1201		"andl	$3, %%ecx\n"
1202		"jz	2f\n"
1203
1204		"movb	%%al, %%bl\n"
1205		"shrl	$8, %%eax\n"
1206		"xorb	(%%esi), %%bl\n"
1207		"xorl	(%%edi,%%ebx,4), %%eax\n"
1208
1209		"decl	%%ecx\n"
1210		"jz	2f\n"
1211
1212		"movb	%%al, %%bl\n"
1213		"shrl	$8, %%eax\n"
1214		"xorb	1(%%esi), %%bl\n"
1215		"xorl	(%%edi,%%ebx,4), %%eax\n"
1216
1217		"decl	%%ecx\n"
1218		"jz	2f\n"
1219
1220		"movb	%%al, %%bl\n"
1221		"shrl	$8, %%eax\n"
1222		"xorb	2(%%esi), %%bl\n"
1223		"xorl	(%%edi,%%ebx,4), %%eax\n"
1224	"2:\n"
1225		:
1226		: "a" (_crc), "g" (p), "g" (len)
1227		: "ax", "bx", "cx", "dx", "si", "di"
1228	);
1229
1230	return (_crc);
1231}
1232
1233#else	/* ASM_CRC */
1234
1235static u_int32_t
1236calc_crc32(u_int32_t crc, caddr_t p, u_int len)
1237{
1238	while (len--)
1239		crc = CRC32(*p++, crc);
1240
1241	return (crc);
1242}
1243
1244#endif	/* ASM_CRC */
1245
1246
1247static u_int32_t crc32tab[] __attribute__ ((aligned(8))) = {
1248	0xD202EF8D,  0xA505DF1B,  0x3C0C8EA1,  0x4B0BBE37,
1249	0xD56F2B94,  0xA2681B02,  0x3B614AB8,  0x4C667A2E,
1250	0xDCD967BF,  0xABDE5729,  0x32D70693,  0x45D03605,
1251	0xDBB4A3A6,  0xACB39330,  0x35BAC28A,  0x42BDF21C,
1252	0xCFB5FFE9,  0xB8B2CF7F,  0x21BB9EC5,  0x56BCAE53,
1253	0xC8D83BF0,  0xBFDF0B66,  0x26D65ADC,  0x51D16A4A,
1254	0xC16E77DB,  0xB669474D,  0x2F6016F7,  0x58672661,
1255	0xC603B3C2,  0xB1048354,  0x280DD2EE,  0x5F0AE278,
1256	0xE96CCF45,  0x9E6BFFD3,  0x0762AE69,  0x70659EFF,
1257	0xEE010B5C,  0x99063BCA,  0x000F6A70,  0x77085AE6,
1258	0xE7B74777,  0x90B077E1,  0x09B9265B,  0x7EBE16CD,
1259	0xE0DA836E,  0x97DDB3F8,  0x0ED4E242,  0x79D3D2D4,
1260	0xF4DBDF21,  0x83DCEFB7,  0x1AD5BE0D,  0x6DD28E9B,
1261	0xF3B61B38,  0x84B12BAE,  0x1DB87A14,  0x6ABF4A82,
1262	0xFA005713,  0x8D076785,  0x140E363F,  0x630906A9,
1263	0xFD6D930A,  0x8A6AA39C,  0x1363F226,  0x6464C2B0,
1264	0xA4DEAE1D,  0xD3D99E8B,  0x4AD0CF31,  0x3DD7FFA7,
1265	0xA3B36A04,  0xD4B45A92,  0x4DBD0B28,  0x3ABA3BBE,
1266	0xAA05262F,  0xDD0216B9,  0x440B4703,  0x330C7795,
1267	0xAD68E236,  0xDA6FD2A0,  0x4366831A,  0x3461B38C,
1268	0xB969BE79,  0xCE6E8EEF,  0x5767DF55,  0x2060EFC3,
1269	0xBE047A60,  0xC9034AF6,  0x500A1B4C,  0x270D2BDA,
1270	0xB7B2364B,  0xC0B506DD,  0x59BC5767,  0x2EBB67F1,
1271	0xB0DFF252,  0xC7D8C2C4,  0x5ED1937E,  0x29D6A3E8,
1272	0x9FB08ED5,  0xE8B7BE43,  0x71BEEFF9,  0x06B9DF6F,
1273	0x98DD4ACC,  0xEFDA7A5A,  0x76D32BE0,  0x01D41B76,
1274	0x916B06E7,  0xE66C3671,  0x7F6567CB,  0x0862575D,
1275	0x9606C2FE,  0xE101F268,  0x7808A3D2,  0x0F0F9344,
1276	0x82079EB1,  0xF500AE27,  0x6C09FF9D,  0x1B0ECF0B,
1277	0x856A5AA8,  0xF26D6A3E,  0x6B643B84,  0x1C630B12,
1278	0x8CDC1683,  0xFBDB2615,  0x62D277AF,  0x15D54739,
1279	0x8BB1D29A,  0xFCB6E20C,  0x65BFB3B6,  0x12B88320,
1280	0x3FBA6CAD,  0x48BD5C3B,  0xD1B40D81,  0xA6B33D17,
1281	0x38D7A8B4,  0x4FD09822,  0xD6D9C998,  0xA1DEF90E,
1282	0x3161E49F,  0x4666D409,  0xDF6F85B3,  0xA868B525,
1283	0x360C2086,  0x410B1010,  0xD80241AA,  0xAF05713C,
1284	0x220D7CC9,  0x550A4C5F,  0xCC031DE5,  0xBB042D73,
1285	0x2560B8D0,  0x52678846,  0xCB6ED9FC,  0xBC69E96A,
1286	0x2CD6F4FB,  0x5BD1C46D,  0xC2D895D7,  0xB5DFA541,
1287	0x2BBB30E2,  0x5CBC0074,  0xC5B551CE,  0xB2B26158,
1288	0x04D44C65,  0x73D37CF3,  0xEADA2D49,  0x9DDD1DDF,
1289	0x03B9887C,  0x74BEB8EA,  0xEDB7E950,  0x9AB0D9C6,
1290	0x0A0FC457,  0x7D08F4C1,  0xE401A57B,  0x930695ED,
1291	0x0D62004E,  0x7A6530D8,  0xE36C6162,  0x946B51F4,
1292	0x19635C01,  0x6E646C97,  0xF76D3D2D,  0x806A0DBB,
1293	0x1E0E9818,  0x6909A88E,  0xF000F934,  0x8707C9A2,
1294	0x17B8D433,  0x60BFE4A5,  0xF9B6B51F,  0x8EB18589,
1295	0x10D5102A,  0x67D220BC,  0xFEDB7106,  0x89DC4190,
1296	0x49662D3D,  0x3E611DAB,  0xA7684C11,  0xD06F7C87,
1297	0x4E0BE924,  0x390CD9B2,  0xA0058808,  0xD702B89E,
1298	0x47BDA50F,  0x30BA9599,  0xA9B3C423,  0xDEB4F4B5,
1299	0x40D06116,  0x37D75180,  0xAEDE003A,  0xD9D930AC,
1300	0x54D13D59,  0x23D60DCF,  0xBADF5C75,  0xCDD86CE3,
1301	0x53BCF940,  0x24BBC9D6,  0xBDB2986C,  0xCAB5A8FA,
1302	0x5A0AB56B,  0x2D0D85FD,  0xB404D447,  0xC303E4D1,
1303	0x5D677172,  0x2A6041E4,  0xB369105E,  0xC46E20C8,
1304	0x72080DF5,  0x050F3D63,  0x9C066CD9,  0xEB015C4F,
1305	0x7565C9EC,  0x0262F97A,  0x9B6BA8C0,  0xEC6C9856,
1306	0x7CD385C7,  0x0BD4B551,  0x92DDE4EB,  0xE5DAD47D,
1307	0x7BBE41DE,  0x0CB97148,  0x95B020F2,  0xE2B71064,
1308	0x6FBF1D91,  0x18B82D07,  0x81B17CBD,  0xF6B64C2B,
1309	0x68D2D988,  0x1FD5E91E,  0x86DCB8A4,  0xF1DB8832,
1310	0x616495A3,  0x1663A535,  0x8F6AF48F,  0xF86DC419,
1311	0x660951BA,  0x110E612C,  0x88073096,  0xFF000000
1312};
1313