if_ed_hpp.c revision 154924
1/*-
2 * Copyright (c) 2005, M. Warner Losh
3 * All rights reserved.
4 * Copyright (c) 1995, David Greenman
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice unmodified, this list of conditions, and the following
12 *    disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/dev/ed/if_ed_hpp.c 154924 2006-01-27 19:10:13Z imp $");
32
33#include "opt_ed.h"
34
35#ifdef ED_HPP
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/sockio.h>
40#include <sys/mbuf.h>
41#include <sys/kernel.h>
42#include <sys/socket.h>
43#include <sys/syslog.h>
44
45#include <sys/bus.h>
46
47#include <machine/bus.h>
48#include <sys/rman.h>
49#include <machine/resource.h>
50
51#include <net/ethernet.h>
52#include <net/if.h>
53#include <net/if_arp.h>
54#include <net/if_dl.h>
55#include <net/if_mib.h>
56#include <net/if_media.h>
57
58#include <net/bpf.h>
59
60#include <dev/ed/if_edreg.h>
61#include <dev/ed/if_edvar.h>
62
63static void	ed_hpp_readmem(struct ed_softc *, bus_size_t, uint8_t *,
64		    uint16_t);
65static void	ed_hpp_writemem(struct ed_softc *, uint8_t *, uint16_t,
66		    uint16_t);
67static void	ed_hpp_set_physical_link(struct ed_softc *sc);
68static u_short	ed_hpp_write_mbufs(struct ed_softc *, struct mbuf *,
69		    bus_size_t);
70
71/*
72 * Interrupt conversion table for the HP PC LAN+
73 */
74static uint16_t ed_hpp_intr_val[] = {
75	0,		/* 0 */
76	0,		/* 1 */
77	0,		/* 2 */
78	3,		/* 3 */
79	4,		/* 4 */
80	5,		/* 5 */
81	6,		/* 6 */
82	7,		/* 7 */
83	0,		/* 8 */
84	9,		/* 9 */
85	10,		/* 10 */
86	11,		/* 11 */
87	12,		/* 12 */
88	0,		/* 13 */
89	0,		/* 14 */
90	15		/* 15 */
91};
92
93#define	ED_HPP_TEST_SIZE	16
94
95/*
96 * Probe and vendor specific initialization for the HP PC Lan+ Cards.
97 * (HP Part nos: 27247B and 27252A).
98 *
99 * The card has an asic wrapper around a DS8390 core.  The asic handles
100 * host accesses and offers both standard register IO and memory mapped
101 * IO.  Memory mapped I/O allows better performance at the expense of greater
102 * chance of an incompatibility with existing ISA cards.
103 *
104 * The card has a few caveats: it isn't tolerant of byte wide accesses, only
105 * short (16 bit) or word (32 bit) accesses are allowed.  Some card revisions
106 * don't allow 32 bit accesses; these are indicated by a bit in the software
107 * ID register (see if_edreg.h).
108 *
109 * Other caveats are: we should read the MAC address only when the card
110 * is inactive.
111 *
112 * For more information; please consult the CRYNWR packet driver.
113 *
114 * The AUI port is turned on using the "link2" option on the ifconfig
115 * command line.
116 */
117int
118ed_probe_HP_pclanp(device_t dev, int port_rid, int flags)
119{
120	struct ed_softc *sc = device_get_softc(dev);
121	int error;
122	int n;				/* temp var */
123	int memsize;			/* mem on board */
124	u_char checksum;		/* checksum of board address */
125	u_char irq;			/* board configured IRQ */
126	uint8_t test_pattern[ED_HPP_TEST_SIZE];	/* read/write areas for */
127	uint8_t test_buffer[ED_HPP_TEST_SIZE];	/* probing card */
128	u_long conf_maddr, conf_msize, conf_irq, junk;
129
130	error = ed_alloc_port(dev, 0, ED_HPP_IO_PORTS);
131	if (error)
132		return (error);
133
134	/* Fill in basic information */
135	sc->asic_offset = ED_HPP_ASIC_OFFSET;
136	sc->nic_offset  = ED_HPP_NIC_OFFSET;
137
138	sc->chip_type = ED_CHIP_TYPE_DP8390;
139	sc->isa16bit = 0;	/* the 8390 core needs to be in byte mode */
140
141	/*
142	 * Look for the HP PCLAN+ signature: "0x50,0x48,0x00,0x53"
143	 */
144
145	if ((ed_asic_inb(sc, ED_HPP_ID) != 0x50) ||
146	    (ed_asic_inb(sc, ED_HPP_ID + 1) != 0x48) ||
147	    ((ed_asic_inb(sc, ED_HPP_ID + 2) & 0xF0) != 0) ||
148	    (ed_asic_inb(sc, ED_HPP_ID + 3) != 0x53))
149		return (ENXIO);
150
151	/*
152	 * Read the MAC address and verify checksum on the address.
153	 */
154
155	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_MAC);
156	for (n  = 0, checksum = 0; n < ETHER_ADDR_LEN; n++)
157		checksum += (sc->enaddr[n] =
158		    ed_asic_inb(sc, ED_HPP_MAC_ADDR + n));
159
160	checksum += ed_asic_inb(sc, ED_HPP_MAC_ADDR + ETHER_ADDR_LEN);
161
162	if (checksum != 0xFF)
163		return (ENXIO);
164
165	/*
166	 * Verify that the software model number is 0.
167	 */
168
169	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_ID);
170	if (((sc->hpp_id = ed_asic_inw(sc, ED_HPP_PAGE_4)) &
171		ED_HPP_ID_SOFT_MODEL_MASK) != 0x0000)
172		return (ENXIO);
173
174	/*
175	 * Read in and save the current options configured on card.
176	 */
177
178	sc->hpp_options = ed_asic_inw(sc, ED_HPP_OPTION);
179
180	sc->hpp_options |= (ED_HPP_OPTION_NIC_RESET |
181	    ED_HPP_OPTION_CHIP_RESET | ED_HPP_OPTION_ENABLE_IRQ);
182
183	/*
184	 * Reset the chip.  This requires writing to the option register
185	 * so take care to preserve the other bits.
186	 */
187
188	ed_asic_outw(sc, ED_HPP_OPTION,
189	    (sc->hpp_options & ~(ED_HPP_OPTION_NIC_RESET |
190	    ED_HPP_OPTION_CHIP_RESET)));
191
192	DELAY(5000);	/* wait for chip reset to complete */
193
194	ed_asic_outw(sc, ED_HPP_OPTION,
195	    (sc->hpp_options | (ED_HPP_OPTION_NIC_RESET |
196	    ED_HPP_OPTION_CHIP_RESET |
197	    ED_HPP_OPTION_ENABLE_IRQ)));
198
199	DELAY(5000);
200
201	if (!(ed_nic_inb(sc, ED_P0_ISR) & ED_ISR_RST))
202		return (ENXIO);	/* reset did not complete */
203
204	/*
205	 * Read out configuration information.
206	 */
207
208	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
209
210	irq = ed_asic_inb(sc, ED_HPP_HW_IRQ);
211
212	/*
213 	 * Check for impossible IRQ.
214	 */
215
216	if (irq >= (sizeof(ed_hpp_intr_val) / sizeof(ed_hpp_intr_val[0])))
217		return (ENXIO);
218
219	/*
220	 * If the kernel IRQ was specified with a '?' use the cards idea
221	 * of the IRQ.  If the kernel IRQ was explicitly specified, it
222 	 * should match that of the hardware.
223	 */
224	error = bus_get_resource(dev, SYS_RES_IRQ, 0, &conf_irq, &junk);
225	if (error)
226		bus_set_resource(dev, SYS_RES_IRQ, 0, ed_hpp_intr_val[irq], 1);
227	else {
228		if (conf_irq != ed_hpp_intr_val[irq])
229			return (ENXIO);
230	}
231
232	/*
233	 * Fill in softconfig info.
234	 */
235
236	sc->vendor = ED_VENDOR_HP;
237	sc->type = ED_TYPE_HP_PCLANPLUS;
238	sc->type_str = "HP-PCLAN+";
239
240	sc->mem_shared = 0;	/* we DON'T have dual ported RAM */
241	sc->mem_start = 0;	/* we use offsets inside the card RAM */
242
243	sc->hpp_mem_start = NULL;/* no memory mapped I/O by default */
244
245	/*
246	 * The board has 32KB of memory.  Is there a way to determine
247	 * this programmatically?
248	 */
249
250	memsize = 32768;
251
252	/*
253	 * Check if memory mapping of the I/O registers possible.
254	 */
255	if (sc->hpp_options & ED_HPP_OPTION_MEM_ENABLE) {
256		u_long mem_addr;
257
258		/*
259		 * determine the memory address from the board.
260		 */
261
262		ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
263		mem_addr = (ed_asic_inw(sc, ED_HPP_HW_MEM_MAP) << 8);
264
265		/*
266		 * Check that the kernel specified start of memory and
267		 * hardware's idea of it match.
268		 */
269		error = bus_get_resource(dev, SYS_RES_MEMORY, 0,
270					 &conf_maddr, &conf_msize);
271		if (error)
272			return (error);
273
274		if (mem_addr != conf_maddr)
275			return (ENXIO);
276
277		error = ed_alloc_memory(dev, 0, memsize);
278		if (error)
279			return (error);
280
281		sc->hpp_mem_start = rman_get_virtual(sc->mem_res);
282	}
283
284	/*
285	 * Fill in the rest of the soft config structure.
286	 */
287
288	/*
289	 * The transmit page index.
290	 */
291
292	sc->tx_page_start = ED_HPP_TX_PAGE_OFFSET;
293
294	if (device_get_flags(dev) & ED_FLAGS_NO_MULTI_BUFFERING)
295		sc->txb_cnt = 1;
296	else
297		sc->txb_cnt = 2;
298
299	/*
300	 * Memory description
301	 */
302
303	sc->mem_size = memsize;
304	sc->mem_ring = sc->mem_start +
305		(sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE);
306	sc->mem_end = sc->mem_start + sc->mem_size;
307
308	/*
309	 * Receive area starts after the transmit area and
310	 * continues till the end of memory.
311	 */
312
313	sc->rec_page_start = sc->tx_page_start +
314				(sc->txb_cnt * ED_TXBUF_SIZE);
315	sc->rec_page_stop = (sc->mem_size / ED_PAGE_SIZE);
316
317
318	sc->cr_proto = 0;	/* value works */
319
320	/*
321	 * Set the wrap registers for string I/O reads.
322	 */
323
324	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
325	ed_asic_outw(sc, ED_HPP_HW_WRAP,
326	    ((sc->rec_page_start / ED_PAGE_SIZE) |
327	    (((sc->rec_page_stop / ED_PAGE_SIZE) - 1) << 8)));
328
329	/*
330	 * Reset the register page to normal operation.
331	 */
332
333	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF);
334
335	/*
336	 * Verify that we can read/write from adapter memory.
337	 * Create test pattern.
338	 */
339
340	for (n = 0; n < ED_HPP_TEST_SIZE; n++)
341		test_pattern[n] = (n*n) ^ ~n;
342
343#undef	ED_HPP_TEST_SIZE
344
345	/*
346	 * Check that the memory is accessible thru the I/O ports.
347	 * Write out the contents of "test_pattern", read back
348	 * into "test_buffer" and compare the two for any
349	 * mismatch.
350	 */
351
352	for (n = 0; n < (32768 / ED_PAGE_SIZE); n ++) {
353		ed_hpp_writemem(sc, test_pattern, (n * ED_PAGE_SIZE),
354				sizeof(test_pattern));
355		ed_hpp_readmem(sc, (n * ED_PAGE_SIZE),
356			test_buffer, sizeof(test_pattern));
357
358		if (bcmp(test_pattern, test_buffer,
359			sizeof(test_pattern)))
360			return (ENXIO);
361	}
362
363	sc->sc_mediachg = ed_hpp_set_physical_link;
364	sc->sc_write_mbufs = ed_hpp_write_mbufs;
365	sc->readmem = ed_hpp_readmem;
366	return (0);
367}
368
369/*
370 * HP PC Lan+ : Set the physical link to use AUI or TP/TL.
371 */
372
373static void
374ed_hpp_set_physical_link(struct ed_softc *sc)
375{
376	struct ifnet *ifp = sc->ifp;
377	int lan_page;
378
379	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
380	lan_page = ed_asic_inw(sc, ED_HPP_PAGE_0);
381
382	if (ifp->if_flags & IFF_LINK2) {
383		/*
384		 * Use the AUI port.
385		 */
386
387		lan_page |= ED_HPP_LAN_AUI;
388		ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
389		ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page);
390	} else {
391		/*
392		 * Use the ThinLan interface
393		 */
394
395		lan_page &= ~ED_HPP_LAN_AUI;
396		ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
397		ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page);
398	}
399
400	/*
401	 * Wait for the lan card to re-initialize itself
402	 */
403	DELAY(150000);	/* wait 150 ms */
404
405	/*
406	 * Restore normal pages.
407	 */
408	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF);
409}
410
411/*
412 * Support routines to handle the HP PC Lan+ card.
413 */
414
415/*
416 * HP PC Lan+: Read from NIC memory, using either PIO or memory mapped
417 * IO.
418 */
419
420static void
421ed_hpp_readmem(struct ed_softc *sc, bus_size_t src, uint8_t *dst,
422    uint16_t amount)
423{
424	int use_32bit_access = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS);
425
426	/* Program the source address in RAM */
427	ed_asic_outw(sc, ED_HPP_PAGE_2, src);
428
429	/*
430	 * The HP PC Lan+ card supports word reads as well as
431	 * a memory mapped i/o port that is aliased to every
432	 * even address on the board.
433	 */
434	if (sc->hpp_mem_start) {
435		/* Enable memory mapped access.  */
436		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
437			~(ED_HPP_OPTION_MEM_DISABLE |
438			  ED_HPP_OPTION_BOOT_ROM_ENB));
439
440		if (use_32bit_access && (amount > 3)) {
441			uint32_t *dl = (uint32_t *) dst;
442			volatile uint32_t *const sl =
443				(uint32_t *) sc->hpp_mem_start;
444			uint32_t *const fence = dl + (amount >> 2);
445
446			/*
447			 * Copy out NIC data.  We could probably write this
448			 * as a `movsl'. The currently generated code is lousy.
449			 */
450			while (dl < fence)
451				*dl++ = *sl;
452
453			dst += (amount & ~3);
454			amount &= 3;
455
456		}
457
458		/* Finish off any words left, as a series of short reads */
459		if (amount > 1) {
460			u_short *d = (u_short *) dst;
461			volatile u_short *const s =
462				(u_short *) sc->hpp_mem_start;
463			u_short *const fence = d + (amount >> 1);
464
465			/* Copy out NIC data.  */
466			while (d < fence)
467				*d++ = *s;
468
469			dst += (amount & ~1);
470			amount &= 1;
471		}
472
473		/*
474		 * read in a byte; however we need to always read 16 bits
475		 * at a time or the hardware gets into a funny state
476		 */
477
478		if (amount == 1) {
479			/* need to read in a short and copy LSB */
480			volatile u_short *const s =
481				(volatile u_short *) sc->hpp_mem_start;
482			*dst = (*s) & 0xFF;
483		}
484
485		/* Restore Boot ROM access.  */
486		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
487	} else {
488		/* Read in data using the I/O port */
489		if (use_32bit_access && (amount > 3)) {
490			ed_asic_insl(sc, ED_HPP_PAGE_4, dst, amount >> 2);
491			dst += (amount & ~3);
492			amount &= 3;
493		}
494		if (amount > 1) {
495			ed_asic_insw(sc, ED_HPP_PAGE_4, dst, amount >> 1);
496			dst += (amount & ~1);
497			amount &= 1;
498		}
499		if (amount == 1) { /* read in a short and keep the LSB */
500			*dst = ed_asic_inw(sc, ED_HPP_PAGE_4) & 0xFF;
501		}
502	}
503}
504
505/*
506 * HP PC Lan+: Write to NIC memory, using either PIO or memory mapped
507 * IO.
508 *	Only used in the probe routine to test the memory. 'len' must
509 *	be even.
510 */
511static void
512ed_hpp_writemem(struct ed_softc *sc, uint8_t *src, uint16_t dst, uint16_t len)
513{
514	/* reset remote DMA complete flag */
515	ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC);
516
517	/* program the write address in RAM */
518	ed_asic_outw(sc, ED_HPP_PAGE_0, dst);
519
520	if (sc->hpp_mem_start) {
521		u_short *s = (u_short *) src;
522		volatile u_short *d = (u_short *) sc->hpp_mem_start;
523		u_short *const fence = s + (len >> 1);
524
525		/*
526		 * Enable memory mapped access.
527		 */
528		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
529			~(ED_HPP_OPTION_MEM_DISABLE |
530			  ED_HPP_OPTION_BOOT_ROM_ENB));
531
532		/*
533		 * Copy to NIC memory.
534		 */
535		while (s < fence)
536			*d = *s++;
537
538		/*
539		 * Restore Boot ROM access.
540		 */
541		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
542	} else {
543		/* write data using I/O writes */
544		ed_asic_outsw(sc, ED_HPP_PAGE_4, src, len / 2);
545	}
546}
547
548/*
549 * Write to HP PC Lan+ NIC memory.  Access to the NIC can be by using
550 * outsw() or via the memory mapped interface to the same register.
551 * Writes have to be in word units; byte accesses won't work and may cause
552 * the NIC to behave weirdly. Long word accesses are permitted if the ASIC
553 * allows it.
554 */
555
556static u_short
557ed_hpp_write_mbufs(struct ed_softc *sc, struct mbuf *m, bus_size_t dst)
558{
559	int len, wantbyte;
560	unsigned short total_len;
561	unsigned char savebyte[2];
562	volatile u_short * const d =
563		(volatile u_short *) sc->hpp_mem_start;
564	int use_32bit_accesses = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS);
565
566	/* select page 0 registers */
567	ed_nic_outb(sc, ED_P0_CR, sc->cr_proto | ED_CR_STA);
568
569	/* reset remote DMA complete flag */
570	ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC);
571
572	/* program the write address in RAM */
573	ed_asic_outw(sc, ED_HPP_PAGE_0, dst);
574
575	if (sc->hpp_mem_start) 	/* enable memory mapped I/O */
576		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
577			~(ED_HPP_OPTION_MEM_DISABLE |
578			ED_HPP_OPTION_BOOT_ROM_ENB));
579
580	wantbyte = 0;
581	total_len = 0;
582
583	if (sc->hpp_mem_start) {	/* Memory mapped I/O port */
584		while (m) {
585			total_len += (len = m->m_len);
586			if (len) {
587				caddr_t data = mtod(m, caddr_t);
588				/* finish the last word of the previous mbuf */
589				if (wantbyte) {
590					savebyte[1] = *data;
591					*d = *((u_short *) savebyte);
592					data++; len--; wantbyte = 0;
593				}
594				/* output contiguous words */
595				if ((len > 3) && (use_32bit_accesses)) {
596					volatile uint32_t *const dl =
597						(volatile uint32_t *) d;
598					uint32_t *sl = (uint32_t *) data;
599					uint32_t *fence = sl + (len >> 2);
600
601					while (sl < fence)
602						*dl = *sl++;
603
604					data += (len & ~3);
605					len &= 3;
606				}
607				/* finish off remain 16 bit writes */
608				if (len > 1) {
609					u_short *s = (u_short *) data;
610					u_short *fence = s + (len >> 1);
611
612					while (s < fence)
613						*d = *s++;
614
615					data += (len & ~1);
616					len &= 1;
617				}
618				/* save last byte if needed */
619				if ((wantbyte = (len == 1)) != 0)
620					savebyte[0] = *data;
621			}
622			m = m->m_next;	/* to next mbuf */
623		}
624		if (wantbyte) /* write last byte */
625			*d = *((u_short *) savebyte);
626	} else {
627		/* use programmed I/O */
628		while (m) {
629			total_len += (len = m->m_len);
630			if (len) {
631				caddr_t data = mtod(m, caddr_t);
632				/* finish the last word of the previous mbuf */
633				if (wantbyte) {
634					savebyte[1] = *data;
635					ed_asic_outw(sc, ED_HPP_PAGE_4,
636						     *((u_short *)savebyte));
637					data++;
638					len--;
639					wantbyte = 0;
640				}
641				/* output contiguous words */
642				if ((len > 3) && use_32bit_accesses) {
643					ed_asic_outsl(sc, ED_HPP_PAGE_4,
644						      data, len >> 2);
645					data += (len & ~3);
646					len &= 3;
647				}
648				/* finish off remaining 16 bit accesses */
649				if (len > 1) {
650					ed_asic_outsw(sc, ED_HPP_PAGE_4,
651						      data, len >> 1);
652					data += (len & ~1);
653					len &= 1;
654				}
655				if ((wantbyte = (len == 1)) != 0)
656					savebyte[0] = *data;
657
658			} /* if len != 0 */
659			m = m->m_next;
660		}
661		if (wantbyte) /* spit last byte */
662			ed_asic_outw(sc, ED_HPP_PAGE_4, *(u_short *)savebyte);
663
664	}
665
666	if (sc->hpp_mem_start)	/* turn off memory mapped i/o */
667		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
668
669	return (total_len);
670}
671
672#endif /* ED_HPP */
673