aslrestype1i.c revision 228110
1
2/******************************************************************************
3 *
4 * Module Name: aslrestype1i - Small I/O-related resource descriptors
5 *
6 *****************************************************************************/
7
8/*
9 * Copyright (C) 2000 - 2011, Intel Corp.
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions, and the following disclaimer,
17 *    without modification.
18 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
19 *    substantially similar to the "NO WARRANTY" disclaimer below
20 *    ("Disclaimer") and any redistribution must be conditioned upon
21 *    including a substantially similar Disclaimer requirement for further
22 *    binary redistribution.
23 * 3. Neither the names of the above-listed copyright holders nor the names
24 *    of any contributors may be used to endorse or promote products derived
25 *    from this software without specific prior written permission.
26 *
27 * Alternatively, this software may be distributed under the terms of the
28 * GNU General Public License ("GPL") version 2 as published by the Free
29 * Software Foundation.
30 *
31 * NO WARRANTY
32 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
33 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
34 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
35 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
36 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
41 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42 * POSSIBILITY OF SUCH DAMAGES.
43 */
44
45
46#include <contrib/dev/acpica/compiler/aslcompiler.h>
47#include "aslcompiler.y.h"
48
49#define _COMPONENT          ACPI_COMPILER
50        ACPI_MODULE_NAME    ("aslrestype1i")
51
52/*
53 * This module contains the I/O-related small resource descriptors:
54 *
55 * DMA
56 * FixedDMA
57 * FixedIO
58 * IO
59 * IRQ
60 * IRQNoFlags
61 */
62
63/*******************************************************************************
64 *
65 * FUNCTION:    RsDoDmaDescriptor
66 *
67 * PARAMETERS:  Op                  - Parent resource descriptor parse node
68 *              CurrentByteOffset   - Offset into the resource template AML
69 *                                    buffer (to track references to the desc)
70 *
71 * RETURN:      Completed resource node
72 *
73 * DESCRIPTION: Construct a short "DMA" descriptor
74 *
75 ******************************************************************************/
76
77ASL_RESOURCE_NODE *
78RsDoDmaDescriptor (
79    ACPI_PARSE_OBJECT       *Op,
80    UINT32                  CurrentByteOffset)
81{
82    AML_RESOURCE            *Descriptor;
83    ACPI_PARSE_OBJECT       *InitializerOp;
84    ASL_RESOURCE_NODE       *Rnode;
85    UINT32                  i;
86    UINT8                   DmaChannelMask = 0;
87    UINT8                   DmaChannels = 0;
88
89
90    InitializerOp = Op->Asl.Child;
91    Rnode = RsAllocateResourceNode (sizeof (AML_RESOURCE_DMA));
92
93    Descriptor = Rnode->Buffer;
94    Descriptor->Dma.DescriptorType  = ACPI_RESOURCE_NAME_DMA |
95                                        ASL_RDESC_DMA_SIZE;
96
97    /* Process all child initialization nodes */
98
99    for (i = 0; InitializerOp; i++)
100    {
101        switch (i)
102        {
103        case 0: /* DMA type */
104
105            RsSetFlagBits (&Descriptor->Dma.Flags, InitializerOp, 5, 0);
106            RsCreateMultiBitField (InitializerOp, ACPI_RESTAG_DMATYPE,
107                CurrentByteOffset + ASL_RESDESC_OFFSET (Dma.Flags), 5, 2);
108            break;
109
110        case 1: /* Bus Master */
111
112            RsSetFlagBits (&Descriptor->Dma.Flags, InitializerOp, 2, 0);
113            RsCreateBitField (InitializerOp, ACPI_RESTAG_BUSMASTER,
114                CurrentByteOffset + ASL_RESDESC_OFFSET (Dma.Flags), 2);
115            break;
116
117        case 2: /* Xfer Type (transfer width) */
118
119            RsSetFlagBits (&Descriptor->Dma.Flags, InitializerOp, 0, 0);
120            RsCreateMultiBitField (InitializerOp, ACPI_RESTAG_XFERTYPE,
121                CurrentByteOffset + ASL_RESDESC_OFFSET (Dma.Flags), 0, 2);
122            break;
123
124        case 3: /* Name */
125
126            UtAttachNamepathToOwner (Op, InitializerOp);
127            break;
128
129        default:
130
131            /* All DMA channel bytes are handled here, after flags and name */
132
133            if (InitializerOp->Asl.ParseOpcode != PARSEOP_DEFAULT_ARG)
134            {
135                /* Up to 8 channels can be specified in the list */
136
137                DmaChannels++;
138                if (DmaChannels > 8)
139                {
140                    AslError (ASL_ERROR, ASL_MSG_DMA_LIST,
141                        InitializerOp, NULL);
142                    return (Rnode);
143                }
144
145                /* Only DMA channels 0-7 are allowed (mask is 8 bits) */
146
147                if (InitializerOp->Asl.Value.Integer > 7)
148                {
149                    AslError (ASL_ERROR, ASL_MSG_DMA_CHANNEL,
150                        InitializerOp, NULL);
151                }
152
153                /* Build the mask */
154
155                DmaChannelMask |=
156                    (1 << ((UINT8) InitializerOp->Asl.Value.Integer));
157            }
158
159            if (i == 4) /* case 4: First DMA byte */
160            {
161                /* Check now for duplicates in list */
162
163                RsCheckListForDuplicates (InitializerOp);
164
165                /* Create a named field at the start of the list */
166
167                RsCreateByteField (InitializerOp, ACPI_RESTAG_DMA,
168                    CurrentByteOffset +
169                    ASL_RESDESC_OFFSET (Dma.DmaChannelMask));
170            }
171            break;
172        }
173
174        InitializerOp = RsCompleteNodeAndGetNext (InitializerOp);
175    }
176
177    /* Now we can set the channel mask */
178
179    Descriptor->Dma.DmaChannelMask = DmaChannelMask;
180    return (Rnode);
181}
182
183
184/*******************************************************************************
185 *
186 * FUNCTION:    RsDoFixedDmaDescriptor
187 *
188 * PARAMETERS:  Op                  - Parent resource descriptor parse node
189 *              CurrentByteOffset   - Offset into the resource template AML
190 *                                    buffer (to track references to the desc)
191 *
192 * RETURN:      Completed resource node
193 *
194 * DESCRIPTION: Construct a short "FixedDMA" descriptor
195 *
196 ******************************************************************************/
197
198ASL_RESOURCE_NODE *
199RsDoFixedDmaDescriptor (
200    ACPI_PARSE_OBJECT       *Op,
201    UINT32                  CurrentByteOffset)
202{
203    AML_RESOURCE            *Descriptor;
204    ACPI_PARSE_OBJECT       *InitializerOp;
205    ASL_RESOURCE_NODE       *Rnode;
206    UINT32                  i;
207
208
209    InitializerOp = Op->Asl.Child;
210    Rnode = RsAllocateResourceNode (sizeof (AML_RESOURCE_FIXED_DMA));
211
212    Descriptor = Rnode->Buffer;
213    Descriptor->FixedDma.DescriptorType =
214        ACPI_RESOURCE_NAME_FIXED_DMA | ASL_RDESC_FIXED_DMA_SIZE;
215
216    /* Process all child initialization nodes */
217
218    for (i = 0; InitializerOp; i++)
219    {
220        switch (i)
221        {
222        case 0: /* DMA Request Lines [WORD] (_DMA) */
223
224            Descriptor->FixedDma.RequestLines = (UINT16) InitializerOp->Asl.Value.Integer;
225            RsCreateWordField (InitializerOp, ACPI_RESTAG_DMA,
226                CurrentByteOffset + ASL_RESDESC_OFFSET (FixedDma.RequestLines));
227            break;
228
229        case 1: /* DMA Channel [WORD] (_TYP) */
230
231            Descriptor->FixedDma.Channels = (UINT16) InitializerOp->Asl.Value.Integer;
232            RsCreateWordField (InitializerOp, ACPI_RESTAG_DMATYPE,
233                CurrentByteOffset + ASL_RESDESC_OFFSET (FixedDma.Channels));
234            break;
235
236        case 2: /* Transfer Width [BYTE] (_SIZ) */
237
238            Descriptor->FixedDma.Width = (UINT8) InitializerOp->Asl.Value.Integer;
239            RsCreateByteField (InitializerOp, ACPI_RESTAG_XFERTYPE,
240                CurrentByteOffset + ASL_RESDESC_OFFSET (FixedDma.Width));
241            break;
242
243        case 3: /* Descriptor Name (optional) */
244
245            UtAttachNamepathToOwner (Op, InitializerOp);
246            break;
247
248        default:    /* Ignore any extra nodes */
249            break;
250        }
251
252        InitializerOp = RsCompleteNodeAndGetNext (InitializerOp);
253    }
254
255    return (Rnode);
256}
257
258
259/*******************************************************************************
260 *
261 * FUNCTION:    RsDoFixedIoDescriptor
262 *
263 * PARAMETERS:  Op                  - Parent resource descriptor parse node
264 *              CurrentByteOffset   - Offset into the resource template AML
265 *                                    buffer (to track references to the desc)
266 *
267 * RETURN:      Completed resource node
268 *
269 * DESCRIPTION: Construct a short "FixedIO" descriptor
270 *
271 ******************************************************************************/
272
273ASL_RESOURCE_NODE *
274RsDoFixedIoDescriptor (
275    ACPI_PARSE_OBJECT       *Op,
276    UINT32                  CurrentByteOffset)
277{
278    AML_RESOURCE            *Descriptor;
279    ACPI_PARSE_OBJECT       *InitializerOp;
280    ACPI_PARSE_OBJECT       *AddressOp = NULL;
281    ASL_RESOURCE_NODE       *Rnode;
282    UINT32                  i;
283
284
285    InitializerOp = Op->Asl.Child;
286    Rnode = RsAllocateResourceNode (sizeof (AML_RESOURCE_FIXED_IO));
287
288    Descriptor = Rnode->Buffer;
289    Descriptor->Io.DescriptorType  = ACPI_RESOURCE_NAME_FIXED_IO |
290                                      ASL_RDESC_FIXED_IO_SIZE;
291
292    /* Process all child initialization nodes */
293
294    for (i = 0; InitializerOp; i++)
295    {
296        switch (i)
297        {
298        case 0: /* Base Address */
299
300            Descriptor->FixedIo.Address =
301                (UINT16) InitializerOp->Asl.Value.Integer;
302            RsCreateWordField (InitializerOp, ACPI_RESTAG_BASEADDRESS,
303                CurrentByteOffset + ASL_RESDESC_OFFSET (FixedIo.Address));
304            AddressOp = InitializerOp;
305            break;
306
307        case 1: /* Length */
308
309            Descriptor->FixedIo.AddressLength =
310                (UINT8) InitializerOp->Asl.Value.Integer;
311            RsCreateByteField (InitializerOp, ACPI_RESTAG_LENGTH,
312                CurrentByteOffset + ASL_RESDESC_OFFSET (FixedIo.AddressLength));
313            break;
314
315        case 2: /* Name */
316
317            UtAttachNamepathToOwner (Op, InitializerOp);
318            break;
319
320        default:
321
322            AslError (ASL_ERROR, ASL_MSG_RESOURCE_LIST, InitializerOp, NULL);
323            break;
324        }
325
326        InitializerOp = RsCompleteNodeAndGetNext (InitializerOp);
327    }
328
329    /* Error checks */
330
331    if (Descriptor->FixedIo.Address > 0x03FF)
332    {
333        AslError (ASL_WARNING, ASL_MSG_ISA_ADDRESS, AddressOp, NULL);
334    }
335
336    return (Rnode);
337}
338
339
340/*******************************************************************************
341 *
342 * FUNCTION:    RsDoIoDescriptor
343 *
344 * PARAMETERS:  Op                  - Parent resource descriptor parse node
345 *              CurrentByteOffset   - Offset into the resource template AML
346 *                                    buffer (to track references to the desc)
347 *
348 * RETURN:      Completed resource node
349 *
350 * DESCRIPTION: Construct a short "IO" descriptor
351 *
352 ******************************************************************************/
353
354ASL_RESOURCE_NODE *
355RsDoIoDescriptor (
356    ACPI_PARSE_OBJECT       *Op,
357    UINT32                  CurrentByteOffset)
358{
359    AML_RESOURCE            *Descriptor;
360    ACPI_PARSE_OBJECT       *InitializerOp;
361    ACPI_PARSE_OBJECT       *MinOp = NULL;
362    ACPI_PARSE_OBJECT       *MaxOp = NULL;
363    ACPI_PARSE_OBJECT       *LengthOp = NULL;
364    ACPI_PARSE_OBJECT       *AlignOp = NULL;
365    ASL_RESOURCE_NODE       *Rnode;
366    UINT32                  i;
367
368
369    InitializerOp = Op->Asl.Child;
370    Rnode = RsAllocateResourceNode (sizeof (AML_RESOURCE_IO));
371
372    Descriptor = Rnode->Buffer;
373    Descriptor->Io.DescriptorType  = ACPI_RESOURCE_NAME_IO |
374                                      ASL_RDESC_IO_SIZE;
375
376    /* Process all child initialization nodes */
377
378    for (i = 0; InitializerOp; i++)
379    {
380        switch (i)
381        {
382        case 0: /* Decode size */
383
384            RsSetFlagBits (&Descriptor->Io.Flags, InitializerOp, 0, 1);
385            RsCreateBitField (InitializerOp, ACPI_RESTAG_DECODE,
386                CurrentByteOffset + ASL_RESDESC_OFFSET (Io.Flags), 0);
387            break;
388
389        case 1:  /* Min Address */
390
391            Descriptor->Io.Minimum =
392                (UINT16) InitializerOp->Asl.Value.Integer;
393            RsCreateWordField (InitializerOp, ACPI_RESTAG_MINADDR,
394                CurrentByteOffset + ASL_RESDESC_OFFSET (Io.Minimum));
395            MinOp = InitializerOp;
396            break;
397
398        case 2: /* Max Address */
399
400            Descriptor->Io.Maximum =
401                (UINT16) InitializerOp->Asl.Value.Integer;
402            RsCreateWordField (InitializerOp, ACPI_RESTAG_MAXADDR,
403                CurrentByteOffset + ASL_RESDESC_OFFSET (Io.Maximum));
404            MaxOp = InitializerOp;
405            break;
406
407        case 3: /* Alignment */
408
409            Descriptor->Io.Alignment =
410                (UINT8) InitializerOp->Asl.Value.Integer;
411            RsCreateByteField (InitializerOp, ACPI_RESTAG_ALIGNMENT,
412                CurrentByteOffset + ASL_RESDESC_OFFSET (Io.Alignment));
413            AlignOp = InitializerOp;
414            break;
415
416        case 4: /* Length */
417
418            Descriptor->Io.AddressLength =
419                (UINT8) InitializerOp->Asl.Value.Integer;
420            RsCreateByteField (InitializerOp, ACPI_RESTAG_LENGTH,
421                CurrentByteOffset + ASL_RESDESC_OFFSET (Io.AddressLength));
422            LengthOp = InitializerOp;
423            break;
424
425        case 5: /* Name */
426
427            UtAttachNamepathToOwner (Op, InitializerOp);
428            break;
429
430        default:
431
432            AslError (ASL_ERROR, ASL_MSG_RESOURCE_LIST, InitializerOp, NULL);
433            break;
434        }
435
436        InitializerOp = RsCompleteNodeAndGetNext (InitializerOp);
437    }
438
439    /* Validate the Min/Max/Len/Align values */
440
441    RsSmallAddressCheck (ACPI_RESOURCE_NAME_IO,
442        Descriptor->Io.Minimum,
443        Descriptor->Io.Maximum,
444        Descriptor->Io.AddressLength,
445        Descriptor->Io.Alignment,
446        MinOp, MaxOp, LengthOp, AlignOp, Op);
447
448    return (Rnode);
449}
450
451
452/*******************************************************************************
453 *
454 * FUNCTION:    RsDoIrqDescriptor
455 *
456 * PARAMETERS:  Op                  - Parent resource descriptor parse node
457 *              CurrentByteOffset   - Offset into the resource template AML
458 *                                    buffer (to track references to the desc)
459 *
460 * RETURN:      Completed resource node
461 *
462 * DESCRIPTION: Construct a short "IRQ" descriptor
463 *
464 ******************************************************************************/
465
466ASL_RESOURCE_NODE *
467RsDoIrqDescriptor (
468    ACPI_PARSE_OBJECT       *Op,
469    UINT32                  CurrentByteOffset)
470{
471    AML_RESOURCE            *Descriptor;
472    ACPI_PARSE_OBJECT       *InitializerOp;
473    ASL_RESOURCE_NODE       *Rnode;
474    UINT32                  Interrupts = 0;
475    UINT16                  IrqMask = 0;
476    UINT32                  i;
477
478
479    InitializerOp = Op->Asl.Child;
480    Rnode = RsAllocateResourceNode (sizeof (AML_RESOURCE_IRQ));
481
482    /* Length = 3 (with flag byte) */
483
484    Descriptor = Rnode->Buffer;
485    Descriptor->Irq.DescriptorType  = ACPI_RESOURCE_NAME_IRQ |
486                                      (ASL_RDESC_IRQ_SIZE + 0x01);
487
488    /* Process all child initialization nodes */
489
490    for (i = 0; InitializerOp; i++)
491    {
492        switch (i)
493        {
494        case 0: /* Interrupt Type (or Mode - edge/level) */
495
496            RsSetFlagBits (&Descriptor->Irq.Flags, InitializerOp, 0, 1);
497            RsCreateBitField (InitializerOp, ACPI_RESTAG_INTERRUPTTYPE,
498                CurrentByteOffset + ASL_RESDESC_OFFSET (Irq.Flags), 0);
499            break;
500
501        case 1: /* Interrupt Level (or Polarity - Active high/low) */
502
503            RsSetFlagBits (&Descriptor->Irq.Flags, InitializerOp, 3, 0);
504            RsCreateBitField (InitializerOp, ACPI_RESTAG_INTERRUPTLEVEL,
505                CurrentByteOffset + ASL_RESDESC_OFFSET (Irq.Flags), 3);
506            break;
507
508        case 2: /* Share Type - Default: exclusive (0) */
509
510            RsSetFlagBits (&Descriptor->Irq.Flags, InitializerOp, 4, 0);
511            RsCreateBitField (InitializerOp, ACPI_RESTAG_INTERRUPTSHARE,
512                CurrentByteOffset + ASL_RESDESC_OFFSET (Irq.Flags), 4);
513            break;
514
515        case 3: /* Name */
516
517            UtAttachNamepathToOwner (Op, InitializerOp);
518            break;
519
520        default:
521
522            /* All IRQ bytes are handled here, after the flags and name */
523
524            if (InitializerOp->Asl.ParseOpcode != PARSEOP_DEFAULT_ARG)
525            {
526                /* Up to 16 interrupts can be specified in the list */
527
528                Interrupts++;
529                if (Interrupts > 16)
530                {
531                    AslError (ASL_ERROR, ASL_MSG_INTERRUPT_LIST,
532                        InitializerOp, NULL);
533                    return (Rnode);
534                }
535
536                /* Only interrupts 0-15 are allowed (mask is 16 bits) */
537
538                if (InitializerOp->Asl.Value.Integer > 15)
539                {
540                    AslError (ASL_ERROR, ASL_MSG_INTERRUPT_NUMBER,
541                        InitializerOp, NULL);
542                }
543                else
544                {
545                    IrqMask |= (1 << (UINT8) InitializerOp->Asl.Value.Integer);
546                }
547            }
548
549            /* Case 4: First IRQ value in list */
550
551            if (i == 4)
552            {
553                /* Check now for duplicates in list */
554
555                RsCheckListForDuplicates (InitializerOp);
556
557                /* Create a named field at the start of the list */
558
559                RsCreateWordField (InitializerOp, ACPI_RESTAG_INTERRUPT,
560                    CurrentByteOffset + ASL_RESDESC_OFFSET (Irq.IrqMask));
561            }
562            break;
563        }
564
565        InitializerOp = RsCompleteNodeAndGetNext (InitializerOp);
566    }
567
568    /* Now we can set the channel mask */
569
570    Descriptor->Irq.IrqMask = IrqMask;
571    return (Rnode);
572}
573
574
575/*******************************************************************************
576 *
577 * FUNCTION:    RsDoIrqNoFlagsDescriptor
578 *
579 * PARAMETERS:  Op                  - Parent resource descriptor parse node
580 *              CurrentByteOffset   - Offset into the resource template AML
581 *                                    buffer (to track references to the desc)
582 *
583 * RETURN:      Completed resource node
584 *
585 * DESCRIPTION: Construct a short "IRQNoFlags" descriptor
586 *
587 ******************************************************************************/
588
589ASL_RESOURCE_NODE *
590RsDoIrqNoFlagsDescriptor (
591    ACPI_PARSE_OBJECT       *Op,
592    UINT32                  CurrentByteOffset)
593{
594    AML_RESOURCE            *Descriptor;
595    ACPI_PARSE_OBJECT       *InitializerOp;
596    ASL_RESOURCE_NODE       *Rnode;
597    UINT16                  IrqMask = 0;
598    UINT32                  Interrupts = 0;
599    UINT32                  i;
600
601
602    InitializerOp = Op->Asl.Child;
603    Rnode = RsAllocateResourceNode (sizeof (AML_RESOURCE_IRQ_NOFLAGS));
604
605    Descriptor = Rnode->Buffer;
606    Descriptor->Irq.DescriptorType  = ACPI_RESOURCE_NAME_IRQ |
607                                      ASL_RDESC_IRQ_SIZE;
608
609    /* Process all child initialization nodes */
610
611    for (i = 0; InitializerOp; i++)
612    {
613        switch (i)
614        {
615        case 0: /* Name */
616
617            UtAttachNamepathToOwner (Op, InitializerOp);
618            break;
619
620        default:
621
622            /* IRQ bytes are handled here, after the flags and name */
623
624            if (InitializerOp->Asl.ParseOpcode != PARSEOP_DEFAULT_ARG)
625            {
626                /* Up to 16 interrupts can be specified in the list */
627
628                Interrupts++;
629                if (Interrupts > 16)
630                {
631                    AslError (ASL_ERROR, ASL_MSG_INTERRUPT_LIST,
632                        InitializerOp, NULL);
633                    return (Rnode);
634                }
635
636                /* Only interrupts 0-15 are allowed (mask is 16 bits) */
637
638                if (InitializerOp->Asl.Value.Integer > 15)
639                {
640                    AslError (ASL_ERROR, ASL_MSG_INTERRUPT_NUMBER,
641                        InitializerOp, NULL);
642                }
643                else
644                {
645                    IrqMask |= (1 << ((UINT8) InitializerOp->Asl.Value.Integer));
646                }
647            }
648
649            /* Case 1: First IRQ value in list */
650
651            if (i == 1)
652            {
653                /* Check now for duplicates in list */
654
655                RsCheckListForDuplicates (InitializerOp);
656
657                /* Create a named field at the start of the list */
658
659                RsCreateWordField (InitializerOp, ACPI_RESTAG_INTERRUPT,
660                    CurrentByteOffset + ASL_RESDESC_OFFSET (Irq.IrqMask));
661            }
662            break;
663        }
664
665        InitializerOp = RsCompleteNodeAndGetNext (InitializerOp);
666    }
667
668    /* Now we can set the interrupt mask */
669
670    Descriptor->Irq.IrqMask = IrqMask;
671    return (Rnode);
672}
673