1 /* mac89x0.c: A Crystal Semiconductor CS89[02]0 driver for linux. */
2 /*
3 	Written 1996 by Russell Nelson, with reference to skeleton.c
4 	written 1993-1994 by Donald Becker.
5 
6 	This software may be used and distributed according to the terms
7 	of the GNU General Public License, incorporated herein by reference.
8 
9 	The author may be reached at nelson@crynwr.com, Crynwr
10 	Software, 11 Grant St., Potsdam, NY 13676
11 
12   Changelog:
13 
14   Mike Cruse        : mcruse@cti-ltd.com
15                     : Changes for Linux 2.0 compatibility.
16                     : Added dev_id parameter in net_interrupt(),
17                     : request_irq() and free_irq(). Just NULL for now.
18 
19   Mike Cruse        : Added MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT macros
20                     : in net_open() and net_close() so kerneld would know
21                     : that the module is in use and wouldn't eject the
22                     : driver prematurely.
23 
24   Mike Cruse        : Rewrote init_module() and cleanup_module using 8390.c
25                     : as an example. Disabled autoprobing in init_module(),
26                     : not a good thing to do to other devices while Linux
27                     : is running from all accounts.
28 
29   Alan Cox          : Removed 1.2 support, added 2.1 extra counters.
30 
31   David Huggins-Daines <dhd@debian.org>
32 
33   Split this off into mac89x0.c, and gutted it of all parts which are
34   not relevant to the existing CS8900 cards on the Macintosh
35   (i.e. basically the Daynaport CS and LC cards).  To be precise:
36 
37     * Removed all the media-detection stuff, because these cards are
38     TP-only.
39 
40     * Lobotomized the ISA interrupt bogosity, because these cards use
41     a hardwired NuBus interrupt and a magic ISAIRQ value in the card.
42 
43     * Basically eliminated everything not relevant to getting the
44     cards minimally functioning on the Macintosh.
45 
46   I might add that these cards are badly designed even from the Mac
47   standpoint, in that Dayna, in their infinite wisdom, used NuBus slot
48   I/O space and NuBus interrupts for these cards, but neglected to
49   provide anything even remotely resembling a NuBus ROM.  Therefore we
50   have to probe for them in a brain-damaged ISA-like fashion.
51 
52   Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 11/01/2001
53   check kmalloc and release the allocated memory on failure in
54   mac89x0_probe and in init_module
55   use local_irq_{save,restore}(flags) in net_get_stat, not just
56   local_irq_{dis,en}able()
57 */
58 
59 static const char version[] =
60 "cs89x0.c:v1.02 11/26/96 Russell Nelson <nelson@crynwr.com>\n";
61 
62 /* ======================= configure the driver here ======================= */
63 
64 /* use 0 for production, 1 for verification, >2 for debug */
65 #ifndef NET_DEBUG
66 #define NET_DEBUG 0
67 #endif
68 
69 /* ======================= end of configuration ======================= */
70 
71 
72 /* Always include 'config.h' first in case the user wants to turn on
73    or override something. */
74 #include <linux/module.h>
75 
76 /*
77   Sources:
78 
79 	Crynwr packet driver epktisa.
80 
81 	Crystal Semiconductor data sheets.
82 
83 */
84 
85 #include <linux/kernel.h>
86 #include <linux/types.h>
87 #include <linux/fcntl.h>
88 #include <linux/interrupt.h>
89 #include <linux/ioport.h>
90 #include <linux/in.h>
91 #include <linux/string.h>
92 #include <linux/nubus.h>
93 #include <linux/errno.h>
94 #include <linux/init.h>
95 #include <linux/netdevice.h>
96 #include <linux/etherdevice.h>
97 #include <linux/skbuff.h>
98 #include <linux/delay.h>
99 #include <linux/bitops.h>
100 #include <linux/gfp.h>
101 
102 #include <asm/io.h>
103 #include <asm/hwtest.h>
104 #include <asm/macints.h>
105 
106 #include "cs89x0.h"
107 
108 static unsigned int net_debug = NET_DEBUG;
109 
110 /* Information that need to be kept for each board. */
111 struct net_local {
112 	int chip_type;		/* one of: CS8900, CS8920, CS8920M */
113 	char chip_revision;	/* revision letter of the chip ('A'...) */
114 	int send_cmd;		/* the propercommand used to send a packet. */
115 	int rx_mode;
116 	int curr_rx_cfg;
117         int send_underrun;      /* keep track of how many underruns in a row we get */
118 	struct sk_buff *skb;
119 };
120 
121 /* Index to functions, as function prototypes. */
122 
123 #if 0
124 extern void reset_chip(struct net_device *dev);
125 #endif
126 static int net_open(struct net_device *dev);
127 static int net_send_packet(struct sk_buff *skb, struct net_device *dev);
128 static irqreturn_t net_interrupt(int irq, void *dev_id);
129 static void set_multicast_list(struct net_device *dev);
130 static void net_rx(struct net_device *dev);
131 static int net_close(struct net_device *dev);
132 static struct net_device_stats *net_get_stats(struct net_device *dev);
133 static int set_mac_address(struct net_device *dev, void *addr);
134 
135 
136 /* Example routines you must write ;->. */
137 #define tx_done(dev) 1
138 
139 /* For reading/writing registers ISA-style */
140 static inline int
141 readreg_io(struct net_device *dev, int portno)
142 {
143 	nubus_writew(swab16(portno), dev->base_addr + ADD_PORT);
144 	return swab16(nubus_readw(dev->base_addr + DATA_PORT));
145 }
146 
147 static inline void
148 writereg_io(struct net_device *dev, int portno, int value)
149 {
150 	nubus_writew(swab16(portno), dev->base_addr + ADD_PORT);
151 	nubus_writew(swab16(value), dev->base_addr + DATA_PORT);
152 }
153 
154 /* These are for reading/writing registers in shared memory */
155 static inline int
156 readreg(struct net_device *dev, int portno)
157 {
158 	return swab16(nubus_readw(dev->mem_start + portno));
159 }
160 
161 static inline void
162 writereg(struct net_device *dev, int portno, int value)
163 {
164 	nubus_writew(swab16(value), dev->mem_start + portno);
165 }
166 
167 static const struct net_device_ops mac89x0_netdev_ops = {
168 	.ndo_open		= net_open,
169 	.ndo_stop		= net_close,
170 	.ndo_start_xmit		= net_send_packet,
171 	.ndo_get_stats		= net_get_stats,
172 	.ndo_set_rx_mode	= set_multicast_list,
173 	.ndo_set_mac_address	= set_mac_address,
174 	.ndo_validate_addr	= eth_validate_addr,
175 };
176 
177 /* Probe for the CS8900 card in slot E.  We won't bother looking
178    anywhere else until we have a really good reason to do so. */
179 struct net_device * __init mac89x0_probe(int unit)
180 {
181 	struct net_device *dev;
182 	static int once_is_enough;
183 	struct net_local *lp;
184 	static unsigned version_printed;
185 	int i, slot;
186 	unsigned rev_type = 0;
187 	unsigned long ioaddr;
188 	unsigned short sig;
189 	int err = -ENODEV;
190 
191 	if (!MACH_IS_MAC)
192 		return ERR_PTR(-ENODEV);
193 
194 	dev = alloc_etherdev(sizeof(struct net_local));
195 	if (!dev)
196 		return ERR_PTR(-ENOMEM);
197 
198 	if (unit >= 0) {
199 		sprintf(dev->name, "eth%d", unit);
200 		netdev_boot_setup_check(dev);
201 	}
202 
203 	if (once_is_enough)
204 		goto out;
205 	once_is_enough = 1;
206 
207 	/* We might have to parameterize this later */
208 	slot = 0xE;
209 	/* Get out now if there's a real NuBus card in slot E */
210 	if (nubus_find_slot(slot, NULL) != NULL)
211 		goto out;
212 
213 	/* The pseudo-ISA bits always live at offset 0x300 (gee,
214            wonder why...) */
215 	ioaddr = (unsigned long)
216 		nubus_slot_addr(slot) | (((slot&0xf) << 20) + DEFAULTIOBASE);
217 	{
218 		int card_present;
219 
220 		card_present = (hwreg_present((void *)ioaddr + 4) &&
221 				hwreg_present((void *)ioaddr + DATA_PORT));
222 		if (!card_present)
223 			goto out;
224 	}
225 
226 	nubus_writew(0, ioaddr + ADD_PORT);
227 	sig = nubus_readw(ioaddr + DATA_PORT);
228 	if (sig != swab16(CHIP_EISA_ID_SIG))
229 		goto out;
230 
231 	/* Initialize the net_device structure. */
232 	lp = netdev_priv(dev);
233 
234 	/* Fill in the 'dev' fields. */
235 	dev->base_addr = ioaddr;
236 	dev->mem_start = (unsigned long)
237 		nubus_slot_addr(slot) | (((slot&0xf) << 20) + MMIOBASE);
238 	dev->mem_end = dev->mem_start + 0x1000;
239 
240 	/* Turn on shared memory */
241 	writereg_io(dev, PP_BusCTL, MEMORY_ON);
242 
243 	/* get the chip type */
244 	rev_type = readreg(dev, PRODUCT_ID_ADD);
245 	lp->chip_type = rev_type &~ REVISON_BITS;
246 	lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
247 
248 	/* Check the chip type and revision in order to set the correct send command
249 	CS8920 revision C and CS8900 revision F can use the faster send. */
250 	lp->send_cmd = TX_AFTER_381;
251 	if (lp->chip_type == CS8900 && lp->chip_revision >= 'F')
252 		lp->send_cmd = TX_NOW;
253 	if (lp->chip_type != CS8900 && lp->chip_revision >= 'C')
254 		lp->send_cmd = TX_NOW;
255 
256 	if (net_debug && version_printed++ == 0)
257 		printk(version);
258 
259 	printk(KERN_INFO "%s: cs89%c0%s rev %c found at %#8lx",
260 	       dev->name,
261 	       lp->chip_type==CS8900?'0':'2',
262 	       lp->chip_type==CS8920M?"M":"",
263 	       lp->chip_revision,
264 	       dev->base_addr);
265 
266 	/* Try to read the MAC address */
267 	if ((readreg(dev, PP_SelfST) & (EEPROM_PRESENT | EEPROM_OK)) == 0) {
268 		printk("\nmac89x0: No EEPROM, giving up now.\n");
269 		goto out1;
270         } else {
271                 for (i = 0; i < ETH_ALEN; i += 2) {
272 			/* Big-endian (why??!) */
273 			unsigned short s = readreg(dev, PP_IA + i);
274                         dev->dev_addr[i] = s >> 8;
275                         dev->dev_addr[i+1] = s & 0xff;
276                 }
277         }
278 
279 	dev->irq = SLOT2IRQ(slot);
280 
281 	/* print the IRQ and ethernet address. */
282 
283 	printk(" IRQ %d ADDR %pM\n", dev->irq, dev->dev_addr);
284 
285 	dev->netdev_ops		= &mac89x0_netdev_ops;
286 
287 	err = register_netdev(dev);
288 	if (err)
289 		goto out1;
290 	return NULL;
291 out1:
292 	nubus_writew(0, dev->base_addr + ADD_PORT);
293 out:
294 	free_netdev(dev);
295 	return ERR_PTR(err);
296 }
297 
298 #if 0
299 /* This is useful for something, but I don't know what yet. */
300 void __init reset_chip(struct net_device *dev)
301 {
302 	int reset_start_time;
303 
304 	writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET);
305 
306 	/* wait 30 ms */
307 	msleep_interruptible(30);
308 
309 	/* Wait until the chip is reset */
310 	reset_start_time = jiffies;
311 	while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 && jiffies - reset_start_time < 2)
312 		;
313 }
314 #endif
315 
316 /* Open/initialize the board.  This is called (in the current kernel)
317    sometime after booting when the 'ifconfig' program is run.
318 
319    This routine should set everything up anew at each open, even
320    registers that "should" only need to be set once at boot, so that
321    there is non-reboot way to recover if something goes wrong.
322    */
323 static int
324 net_open(struct net_device *dev)
325 {
326 	struct net_local *lp = netdev_priv(dev);
327 	int i;
328 
329 	/* Disable the interrupt for now */
330 	writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ);
331 
332 	/* Grab the interrupt */
333 	if (request_irq(dev->irq, net_interrupt, 0, "cs89x0", dev))
334 		return -EAGAIN;
335 
336 	/* Set up the IRQ - Apparently magic */
337 	if (lp->chip_type == CS8900)
338 		writereg(dev, PP_CS8900_ISAINT, 0);
339 	else
340 		writereg(dev, PP_CS8920_ISAINT, 0);
341 
342 	/* set the Ethernet address */
343 	for (i=0; i < ETH_ALEN/2; i++)
344 		writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
345 
346 	/* Turn on both receive and transmit operations */
347 	writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);
348 
349 	/* Receive only error free packets addressed to this card */
350 	lp->rx_mode = 0;
351 	writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
352 
353 	lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
354 
355 	writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
356 
357 	writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL |
358 	       TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);
359 
360 	writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL |
361 		 TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);
362 
363 	/* now that we've got our act together, enable everything */
364 	writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ);
365 	netif_start_queue(dev);
366 	return 0;
367 }
368 
369 static int
370 net_send_packet(struct sk_buff *skb, struct net_device *dev)
371 {
372 	struct net_local *lp = netdev_priv(dev);
373 	unsigned long flags;
374 
375 	if (net_debug > 3)
376 		printk("%s: sent %d byte packet of type %x\n",
377 		       dev->name, skb->len,
378 		       (skb->data[ETH_ALEN+ETH_ALEN] << 8)
379 		       | skb->data[ETH_ALEN+ETH_ALEN+1]);
380 
381 	/* keep the upload from being interrupted, since we
382 	   ask the chip to start transmitting before the
383 	   whole packet has been completely uploaded. */
384 	local_irq_save(flags);
385 	netif_stop_queue(dev);
386 
387 	/* initiate a transmit sequence */
388 	writereg(dev, PP_TxCMD, lp->send_cmd);
389 	writereg(dev, PP_TxLength, skb->len);
390 
391 	/* Test to see if the chip has allocated memory for the packet */
392 	if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
393 		/* Gasp!  It hasn't.  But that shouldn't happen since
394 		   we're waiting for TxOk, so return 1 and requeue this packet. */
395 		local_irq_restore(flags);
396 		return NETDEV_TX_BUSY;
397 	}
398 
399 	/* Write the contents of the packet */
400 	skb_copy_from_linear_data(skb, (void *)(dev->mem_start + PP_TxFrame),
401 				  skb->len+1);
402 
403 	local_irq_restore(flags);
404 	dev_kfree_skb (skb);
405 
406 	return NETDEV_TX_OK;
407 }
408 
409 /* The typical workload of the driver:
410    Handle the network interface interrupts. */
411 static irqreturn_t net_interrupt(int irq, void *dev_id)
412 {
413 	struct net_device *dev = dev_id;
414 	struct net_local *lp;
415 	int ioaddr, status;
416 
417 	if (dev == NULL) {
418 		printk ("net_interrupt(): irq %d for unknown device.\n", irq);
419 		return IRQ_NONE;
420 	}
421 
422 	ioaddr = dev->base_addr;
423 	lp = netdev_priv(dev);
424 
425 	/* we MUST read all the events out of the ISQ, otherwise we'll never
426            get interrupted again.  As a consequence, we can't have any limit
427            on the number of times we loop in the interrupt handler.  The
428            hardware guarantees that eventually we'll run out of events.  Of
429            course, if you're on a slow machine, and packets are arriving
430            faster than you can read them off, you're screwed.  Hasta la
431            vista, baby!  */
432 	while ((status = swab16(nubus_readw(dev->base_addr + ISQ_PORT)))) {
433 		if (net_debug > 4)printk("%s: event=%04x\n", dev->name, status);
434 		switch(status & ISQ_EVENT_MASK) {
435 		case ISQ_RECEIVER_EVENT:
436 			/* Got a packet(s). */
437 			net_rx(dev);
438 			break;
439 		case ISQ_TRANSMITTER_EVENT:
440 			dev->stats.tx_packets++;
441 			netif_wake_queue(dev);
442 			if ((status & TX_OK) == 0)
443 				dev->stats.tx_errors++;
444 			if (status & TX_LOST_CRS)
445 				dev->stats.tx_carrier_errors++;
446 			if (status & TX_SQE_ERROR)
447 				dev->stats.tx_heartbeat_errors++;
448 			if (status & TX_LATE_COL)
449 				dev->stats.tx_window_errors++;
450 			if (status & TX_16_COL)
451 				dev->stats.tx_aborted_errors++;
452 			break;
453 		case ISQ_BUFFER_EVENT:
454 			if (status & READY_FOR_TX) {
455 				/* we tried to transmit a packet earlier,
456                                    but inexplicably ran out of buffers.
457                                    That shouldn't happen since we only ever
458                                    load one packet.  Shrug.  Do the right
459                                    thing anyway. */
460 				netif_wake_queue(dev);
461 			}
462 			if (status & TX_UNDERRUN) {
463 				if (net_debug > 0) printk("%s: transmit underrun\n", dev->name);
464                                 lp->send_underrun++;
465                                 if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381;
466                                 else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL;
467                         }
468 			break;
469 		case ISQ_RX_MISS_EVENT:
470 			dev->stats.rx_missed_errors += (status >> 6);
471 			break;
472 		case ISQ_TX_COL_EVENT:
473 			dev->stats.collisions += (status >> 6);
474 			break;
475 		}
476 	}
477 	return IRQ_HANDLED;
478 }
479 
480 /* We have a good packet(s), get it/them out of the buffers. */
481 static void
482 net_rx(struct net_device *dev)
483 {
484 	struct sk_buff *skb;
485 	int status, length;
486 
487 	status = readreg(dev, PP_RxStatus);
488 	if ((status & RX_OK) == 0) {
489 		dev->stats.rx_errors++;
490 		if (status & RX_RUNT)
491 				dev->stats.rx_length_errors++;
492 		if (status & RX_EXTRA_DATA)
493 				dev->stats.rx_length_errors++;
494 		if ((status & RX_CRC_ERROR) &&
495 		    !(status & (RX_EXTRA_DATA|RX_RUNT)))
496 			/* per str 172 */
497 			dev->stats.rx_crc_errors++;
498 		if (status & RX_DRIBBLE)
499 				dev->stats.rx_frame_errors++;
500 		return;
501 	}
502 
503 	length = readreg(dev, PP_RxLength);
504 	/* Malloc up new buffer. */
505 	skb = alloc_skb(length, GFP_ATOMIC);
506 	if (skb == NULL) {
507 		printk("%s: Memory squeeze, dropping packet.\n", dev->name);
508 		dev->stats.rx_dropped++;
509 		return;
510 	}
511 	skb_put(skb, length);
512 
513 	skb_copy_to_linear_data(skb, (void *)(dev->mem_start + PP_RxFrame),
514 				length);
515 
516 	if (net_debug > 3)printk("%s: received %d byte packet of type %x\n",
517                                  dev->name, length,
518                                  (skb->data[ETH_ALEN+ETH_ALEN] << 8)
519 				 | skb->data[ETH_ALEN+ETH_ALEN+1]);
520 
521         skb->protocol=eth_type_trans(skb,dev);
522 	netif_rx(skb);
523 	dev->stats.rx_packets++;
524 	dev->stats.rx_bytes += length;
525 }
526 
527 /* The inverse routine to net_open(). */
528 static int
529 net_close(struct net_device *dev)
530 {
531 
532 	writereg(dev, PP_RxCFG, 0);
533 	writereg(dev, PP_TxCFG, 0);
534 	writereg(dev, PP_BufCFG, 0);
535 	writereg(dev, PP_BusCTL, 0);
536 
537 	netif_stop_queue(dev);
538 
539 	free_irq(dev->irq, dev);
540 
541 	/* Update the statistics here. */
542 
543 	return 0;
544 
545 }
546 
547 /* Get the current statistics.	This may be called with the card open or
548    closed. */
549 static struct net_device_stats *
550 net_get_stats(struct net_device *dev)
551 {
552 	unsigned long flags;
553 
554 	local_irq_save(flags);
555 	/* Update the statistics from the device registers. */
556 	dev->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6);
557 	dev->stats.collisions += (readreg(dev, PP_TxCol) >> 6);
558 	local_irq_restore(flags);
559 
560 	return &dev->stats;
561 }
562 
563 static void set_multicast_list(struct net_device *dev)
564 {
565 	struct net_local *lp = netdev_priv(dev);
566 
567 	if(dev->flags&IFF_PROMISC)
568 	{
569 		lp->rx_mode = RX_ALL_ACCEPT;
570 	} else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) {
571 		/* The multicast-accept list is initialized to accept-all, and we
572 		   rely on higher-level filtering for now. */
573 		lp->rx_mode = RX_MULTCAST_ACCEPT;
574 	}
575 	else
576 		lp->rx_mode = 0;
577 
578 	writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode);
579 
580 	/* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */
581 	writereg(dev, PP_RxCFG, lp->curr_rx_cfg |
582 	     (lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0));
583 }
584 
585 
586 static int set_mac_address(struct net_device *dev, void *addr)
587 {
588 	struct sockaddr *saddr = addr;
589 	int i;
590 
591 	if (!is_valid_ether_addr(saddr->sa_data))
592 		return -EADDRNOTAVAIL;
593 
594 	memcpy(dev->dev_addr, saddr->sa_data, ETH_ALEN);
595 	printk("%s: Setting MAC address to %pM\n", dev->name, dev->dev_addr);
596 
597 	/* set the Ethernet address */
598 	for (i=0; i < ETH_ALEN/2; i++)
599 		writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
600 
601 	return 0;
602 }
603 
604 #ifdef MODULE
605 
606 static struct net_device *dev_cs89x0;
607 static int debug;
608 
609 module_param(debug, int, 0);
610 MODULE_PARM_DESC(debug, "CS89[02]0 debug level (0-5)");
611 MODULE_LICENSE("GPL");
612 
613 int __init
614 init_module(void)
615 {
616 	net_debug = debug;
617         dev_cs89x0 = mac89x0_probe(-1);
618 	if (IS_ERR(dev_cs89x0)) {
619                 printk(KERN_WARNING "mac89x0.c: No card found\n");
620 		return PTR_ERR(dev_cs89x0);
621 	}
622 	return 0;
623 }
624 
625 void
626 cleanup_module(void)
627 {
628 	unregister_netdev(dev_cs89x0);
629 	nubus_writew(0, dev_cs89x0->base_addr + ADD_PORT);
630 	free_netdev(dev_cs89x0);
631 }
632 #endif /* MODULE */
633