Add PnP IDs from Giuseppe Bilotta:
[pnputils] / lspnp.c
1 /*======================================================================
2
3     A utility for dumping resource information for PnP devices
4
5     lspnp.c 1.9 2006/07/06 15:27:55 MDT
6
7     The initial developer of the original code is David A. Hinds
8     <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
9     are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
10
11     (c) Copyright 2006 Hewlett-Packard Development Company, L.P.
12         Bjorn Helgaas <bjorn.helgaas@hp.com>
13
14     This program is free software; you can redistribute it and/or modify
15     it under the terms of the GNU General Public License version 2 as
16     published by the Free Software Foundation.
17
18     Usage: lspnp [-b] [-v[v]] [device #]
19
20 ======================================================================*/
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <dirent.h>
26 #include <fcntl.h>
27 #include <string.h>
28 #include <endian.h>
29 #include <ctype.h>
30 #include <asm/types.h>
31
32 #include "pnp_resource.h"
33
34 static int verbose = 0, boot = 0;
35
36 static struct {
37     __u8        base;
38     char *      name;
39 } base_type[] = {
40     { 1, "mass storage device" },
41     { 2, "network interface controller" },
42     { 3, "display controller" },
43     { 4, "multimedia controller" },
44     { 5, "memory controller" },
45     { 6, "bridge controller" },
46     { 7, "communications device" },
47     { 8, "system peripheral" },
48     { 9, "input device" },
49     { 10, "service processor" }
50 };
51 #define NBASE   (sizeof(base_type)/sizeof(base_type[0]))
52
53 static struct {
54     __u8        base, sub;
55     char *      name;
56 } sub_type[] = {
57     { 1, 0, "SCSI" },
58     { 1, 1, "IDE" },
59     { 1, 2, "floppy" },
60     { 1, 3, "IPI" },
61     { 2, 0, "ethernet" },
62     { 2, 1, "token ring" },
63     { 2, 2, "FDDI" },
64     { 3, 0, "VGA" },
65     { 3, 1, "SVGA" },
66     { 3, 2, "XGA" },
67     { 4, 0, "video" },
68     { 4, 1, "audio" },
69     { 5, 0, "RAM" },
70     { 5, 1, "flash" },
71     { 6, 0, "host processor" },
72     { 6, 1, "ISA" },
73     { 6, 2, "EISA" },
74     { 6, 3, "MicroChannel" },
75     { 6, 4, "PCI" },
76     { 6, 5, "PCMCIA" },
77     { 6, 6, "VME" },
78     { 7, 0, "RS-232" },
79     { 7, 1, "AT parallel port" },
80     { 8, 0, "programmable interrupt controller" },
81     { 8, 1, "DMA controller" },
82     { 8, 2, "system timer" },
83     { 8, 3, "real time clock" },
84     { 8, 4, "L2 cache" },
85     { 8, 5, "NVRAM" },
86     { 8, 6, "power management" },
87     { 8, 7, "CMOS" },
88     { 8, 8, "operator panel" },
89     { 9, 0, "keyboard" },
90     { 9, 1, "digitizer" },
91     { 9, 2, "mouse" },
92     { 9, 3, "tablet" },
93     { 10, 0, "general memory" }
94 };
95 #define NSUB    (sizeof(sub_type)/sizeof(sub_type[0]))
96
97 static struct eisa_id {
98     char        id[8];
99     char *      name;
100     struct eisa_id * next;
101 } *eisa_id = NULL;
102
103 #define swap16(n) ((((n)&0x00ff)<<8) | (((n)&0xff00)>>8))
104 #define swap32(n) \
105     ((((n)&0xff000000)>>24) | (((n)&0x00ff0000)>>8) | \
106      (((n)&0x0000ff00)<<8)  | (((n)&0x000000ff)<<24))
107
108 #if (__BYTE_ORDER == _BIG_ENDIAN)
109 #define flip16(n)       swap16(n)
110 #define flip32(n)       swap32(n)
111 #else
112 #define flip16(n)       (n)
113 #define flip32(n)       (n)
114 #endif
115
116 /*====================================================================*/
117
118 #define HEX(id,a) hex[((id)>>a) & 15]
119 #define CHAR(id,a) (0x40 + (((id)>>a) & 31))
120
121 static char *eisa_str(__u32 id)
122 {
123     const char *hex = "0123456789abcdef";
124     static char str[8];
125     id = swap32(id);
126     str[0] = CHAR(id, 26);
127     str[1] = CHAR(id, 21);
128     str[2] = CHAR(id,16);
129     str[3] = HEX(id, 12);
130     str[4] = HEX(id, 8);
131     str[5] = HEX(id, 4);
132     str[6] = HEX(id, 0);
133     str[7] = '\0';
134     return str;
135 }
136
137 static void load_ids(void)
138 {
139     char s[133], *t;
140     int n;
141     struct eisa_id *id;
142     FILE *f = fopen("/usr/share/misc/pnp.ids", "r");
143     
144     if (f == NULL)
145         return;
146     while (fgets(s, sizeof(s), f)) {
147         if ((strlen(s) < 9) ||
148             !(isupper(s[0]) && isupper(s[1]) && isupper(s[2]) &&
149               isxdigit(s[3]) && isxdigit(s[4]) && isxdigit(s[5]) &&
150               isxdigit(s[6]))) continue;
151         id = malloc(sizeof(struct eisa_id));
152         strncpy(id->id, s, 7);
153         for (n = 3; n < 7; n++)
154             id->id[n] = tolower(id->id[n]);
155         id->id[7] = '\0';
156         s[strlen(s)-1] = '\0';
157         for (t = s+7; isspace(*t); t++) ;
158         id->name = strdup(t);
159         id->next = eisa_id; eisa_id = id;
160     }
161     fclose(f);
162 }
163
164 static void dump_flags(int flags)
165 {
166     printf("    flags:");
167     if (!flags)
168         printf(" none");
169     if (flags & 0x0001)
170         printf(" [no disable]");
171     if (flags & 0x0002)
172         printf(" [no config]");
173     if (flags & 0x0004)
174         printf(" [output]");
175     if (flags & 0x0008)
176         printf(" [input]");
177     if (flags & 0x0010)
178         printf(" [bootable]");
179     if (flags & 0x0020)
180         printf(" [dock]");
181     if (flags & 0x0040)
182         printf(" [removable]");
183     if ((flags & 0x0180) == 0x0000)
184         printf(" [static]");
185     if ((flags & 0x0180) == 0x0080)
186         printf(" [dynamic]");
187     if ((flags & 0x0180) == 0x0180)
188         printf(" [dynamic only]");
189     printf("\n");
190 }
191
192 static void dump_class(int t1, int t2)
193 {
194     int i;
195     for (i = 0; i < NBASE; i++)
196         if (t1 == base_type[i].base) break;
197     printf("%s: ", (i < NBASE) ? base_type[i].name : "reserved");
198     for (i = 0; i < NSUB; i++)
199         if ((t1 == sub_type[i].base) && (t2 == sub_type[i].sub))
200             break;
201     printf("%s", (i < NSUB) ? sub_type[i].name : "other");
202 }
203
204 /*
205   Small resource tags
206 */
207
208 static void dump_version(union pnp_small_resource *r)
209 {
210     printf("\tPnP version %d.%d, vendor version %d.%d\n",
211            r->version.pnp>>4, r->version.pnp & 0x0f,
212            r->version.vendor>>4, r->version.vendor & 0x0f);
213 }
214
215 static void dump_ldid(union pnp_small_resource *r, int sz)
216 {
217     printf("\tlogical ID %s", eisa_str(r->ldid.id));
218     if (verbose > 1) {
219         if (r->ldid.flag0 & PNP_RES_LDID_BOOT)
220             printf(" [boot]");
221     }
222     printf("\n");
223 }
224
225 static void dump_gdid(union pnp_small_resource *r)
226 {
227     struct eisa_id *eid;
228     char *eis = eisa_str(r->gdid.id);
229     
230     printf("\t%s", eis);
231     for (eid = eisa_id; eid; eid = eid->next)
232         if (strcmp(eis, eid->id) == 0) break;
233     if (eid)
234         printf(" %s\n", eid->name);
235     else
236         printf("\n");
237 }
238
239 static void dump_irq(union pnp_small_resource *r, int sz)
240 {
241     int mask = flip16(r->irq.mask);
242     printf("\tirq ");
243     if (!mask) {
244         printf("disabled");
245     } else if (mask & (mask-1)) {
246         printf("mask 0x%04x", mask);
247     } else {
248         printf("%d", ffs(mask)-1);
249     }
250     if (verbose > 1) {
251         if (sz == 3) {
252             if (r->irq.info & PNP_RES_IRQ_HIGH_EDGE)
253                 printf(" [high edge]");
254             if (r->irq.info & PNP_RES_IRQ_LOW_EDGE)
255                 printf(" [low edge]");
256             if (r->irq.info & PNP_RES_IRQ_HIGH_LEVEL)
257                 printf(" [high level]");
258             if (r->irq.info & PNP_RES_IRQ_LOW_LEVEL)
259                 printf(" [low level]");
260         } else {
261             printf(" [high edge]");
262         }
263     }
264     printf("\n");
265 }
266
267 static void dump_dma(union pnp_small_resource *r)
268 {
269     int mask = r->dma.mask;
270     printf("\tdma ");
271     if (!mask) {
272         printf("disabled");
273     } else if (mask & (mask-1)) {
274         printf("mask 0x%04x", mask);
275     } else {
276         printf("%d", ffs(mask)-1);
277     }
278     if (verbose > 1) {
279         switch (r->dma.info & PNP_RES_DMA_WIDTH_MASK) {
280         case PNP_RES_DMA_WIDTH_8:
281             printf(" [8 bit]"); break;
282         case PNP_RES_DMA_WIDTH_8_16:
283             printf(" [8/16 bit]"); break;
284         case PNP_RES_DMA_WIDTH_16:
285             printf(" [16 bit]"); break;
286         }
287         if (r->dma.info & PNP_RES_DMA_BUSMASTER)
288             printf(" [master]");
289         if (r->dma.info & PNP_RES_DMA_COUNT_BYTE)
290             printf(" [count byte]");
291         if (r->dma.info & PNP_RES_DMA_COUNT_WORD)
292             printf(" [count word]");
293         switch (r->dma.info & PNP_RES_DMA_SPEED_MASK) {
294         case PNP_RES_DMA_SPEED_COMPAT: printf(" [compat]"); break;
295         case PNP_RES_DMA_SPEED_TYPEA: printf(" [type A]"); break;
296         case PNP_RES_DMA_SPEED_TYPEB: printf(" [type B]"); break;
297         case PNP_RES_DMA_SPEED_TYPEF: printf(" [type F]"); break;
298         }
299     }
300     printf("\n");
301 }
302
303 static void dump_dep_start(union pnp_small_resource *r, int sz)
304 {
305     printf("\t[start dep fn");
306     if (sz) {
307         printf(": priority: ");
308         switch (r->dep_start.priority) {
309         case PNP_RES_CONFIG_GOOD:
310             printf("good"); break;
311         case PNP_RES_CONFIG_ACCEPTABLE:
312             printf("acceptable"); break;
313         case PNP_RES_CONFIG_SUBOPTIMAL:
314             printf("suboptimal"); break;
315         default:
316             printf("reserved"); break;
317         }
318     }
319     printf("]\n");
320 }
321
322 static void dump_dep_end(union pnp_small_resource *r)
323 {
324     printf("\t[end dep fn]\n");
325 }
326
327 static void dump_io(union pnp_small_resource *r)
328 {
329     int min = flip16(r->io.min), max = flip16(r->io.max);
330     printf("\tio ");
331     if (r->io.len == 0)
332         printf("disabled");
333     else if (min == max)
334         printf("0x%04x-0x%04x", min, min+r->io.len-1);
335     else
336         printf("base 0x%04x-0x%04x align 0x%02x len 0x%02x",
337                min, max, r->io.align, r->io.len);
338     if (verbose > 1) {
339         if (r->io.info & PNP_RES_IO_DECODE_16)
340             printf(" [16-bit decode]");
341     }
342     printf("\n");
343 }
344
345 static void dump_io_fixed(union pnp_small_resource *r)
346 {
347     int base = flip16(r->io_fixed.base);
348     printf("\tio ");
349     if (r->io_fixed.len == 0)
350         printf("disabled\n");
351     else
352         printf("0x%04x-0x%04x\n", base, base+r->io_fixed.len-1);
353 }
354
355 /*
356   Large resource tags
357 */
358
359 static void dump_mem_info(__u8 info)
360 {
361     switch (info & PNP_RES_MEM_WIDTH_MASK) {
362     case PNP_RES_MEM_WIDTH_8:
363         printf(" [8 bit]"); break;
364     case PNP_RES_MEM_WIDTH_16:
365         printf(" [16 bit]"); break;
366     case PNP_RES_MEM_WIDTH_8_16:
367         printf(" [8/16 bit]"); break;
368     case PNP_RES_MEM_WIDTH_32:
369         printf(" [32 bit]"); break;
370     }
371     printf((info & PNP_RES_MEM_WRITEABLE) ? " [r/w]" : " [r/o]");
372     if (info & PNP_RES_MEM_CACHEABLE)
373         printf(" [cacheable]");
374     if (info & PNP_RES_MEM_HIGH_ADDRESS)
375         printf(" [high]");
376     if (info & PNP_RES_MEM_SHADOWABLE)
377         printf(" [shadow]");
378     if (info & PNP_RES_MEM_EXPANSION_ROM)
379         printf(" [rom]");
380 }
381
382 static void dump_ansi(union pnp_large_resource *r, int sz)
383 {
384     printf("\tidentifier '%.*s'\n", sz, r->ansi.str);
385 }
386
387 static void dump_mem(union pnp_large_resource *r)
388 {
389     int min = flip16(r->mem.min) << 8;
390     int max = flip16(r->mem.max) << 8;
391     int align = flip16(r->mem.align), len = flip16(r->mem.len);
392     printf("\tmem ");
393     if (len == 0)
394         printf("disabled");
395     else if (min == max)
396         printf("0x%06x-0x%06x", min, min+len-1);
397     else
398         printf("base 0x%06x-%06x, align 0x%04x, len 0x%06x",
399                min, max, align ? align : 0x10000, len<<8);
400     if (verbose > 1)
401         dump_mem_info(r->mem.info);
402     printf("\n");
403 }
404
405 static void dump_mem32(union pnp_large_resource *r)
406 {
407     u_int min = flip32(r->mem32.min), max = flip32(r->mem32.max);
408     u_int align = flip32(r->mem32.align), len = flip32(r->mem32.len);
409     printf("\tmem ");
410     if (len == 0)
411         printf("disabled");
412     else if (min == max)
413         printf("0x%08x-0x%08x", min, min+len-1);
414     else
415         printf("\tmem base 0x%08x-0x%08x align 0x%06x len 0x%06x",
416            min, max, align, len);
417     if (verbose > 1)
418         dump_mem_info(r->mem32.info);
419     printf("\n");
420 }
421
422 static void dump_mem32_fixed(union pnp_large_resource *r)
423 {
424     u_int base = flip32(r->mem32_fixed.base);
425     u_int len = flip32(r->mem32_fixed.len);
426     printf("\tmem ");
427     if (len == 0)
428         printf("disabled");
429     else
430         printf("0x%08x-0x%08x", base, base+len-1);
431     if (verbose > 1)
432         dump_mem_info(r->mem32_fixed.info);
433     printf("\n");
434 }
435
436 /*====================================================================*/
437
438 static u_char *dump_chain(u_char *buf, int nr)
439 {
440     union pnp_resource *p = (union pnp_resource *)buf;
441     int tag = 0, sz;
442     
443     while (((u_char *)p < buf+nr) && (tag != PNP_RES_SMTAG_END)) {
444         if (p->lg.tag & PNP_RES_LARGE_ITEM) {
445             union pnp_large_resource *r = &p->lg.d;
446             tag = p->lg.tag & ~PNP_RES_LARGE_ITEM;
447             sz = flip16(p->lg.sz) + 2;
448             switch (tag) {
449             case PNP_RES_LGTAG_MEM:
450                 dump_mem(r); break;
451             case PNP_RES_LGTAG_ID_ANSI:
452                 dump_ansi(r, sz-2); break;
453             case PNP_RES_LGTAG_ID_UNICODE:
454                 /* dump_unicode(r); */ break;
455             case PNP_RES_LGTAG_MEM32:
456                 dump_mem32(r); break;
457             case PNP_RES_LGTAG_MEM32_FIXED:
458                 dump_mem32_fixed(r); break;
459             }
460         } else {
461             union pnp_small_resource *r = &p->sm.d;
462             tag = (p->sm.tag >> 3); sz = (p->sm.tag & 7);
463             switch (tag) {
464             case PNP_RES_SMTAG_VERSION:
465                 dump_version(r); break;
466             case PNP_RES_SMTAG_LDID:
467                 dump_ldid(r, sz); break;
468             case PNP_RES_SMTAG_CDID:
469                 dump_gdid(r); break;
470             case PNP_RES_SMTAG_IRQ:
471                 dump_irq(r, sz); break;
472             case PNP_RES_SMTAG_DMA:
473                 dump_dma(r); break;
474             case PNP_RES_SMTAG_DEP_START:
475                 dump_dep_start(r, sz); break;
476             case PNP_RES_SMTAG_DEP_END:
477                 dump_dep_end(r); break;
478             case PNP_RES_SMTAG_IO:
479                 dump_io(r); break;
480             case PNP_RES_SMTAG_IO_FIXED:
481                 dump_io_fixed(r); break;
482             }
483         }
484         p = (union pnp_resource *) ((u_char *)p + sz + 1);
485     }
486     return (u_char *)p;
487 }
488
489 static void dump_resources(char *name)
490 {
491     char fn[40];
492     u_char buf[4096], *p;
493     int fd, nr;
494     
495     sprintf(fn, "/proc/bus/pnp/%s%s", (boot ? "boot/" : ""), name);
496     fd = open(fn, O_RDONLY);
497     nr = read(fd, buf, sizeof(buf));
498     close(fd);
499     if (nr > 0) {
500         if (verbose > 1)
501             printf("    allocated resources:\n");
502         p = dump_chain(buf, nr);
503         if (verbose > 1) {
504             if (p+4 < buf+nr) {
505                 printf("    possible resources:\n");
506             }
507             p = dump_chain(p, nr);
508             if (p+2 < buf+nr) {
509                 printf("    compatible devices:\n");
510                 p = dump_chain(p, nr);
511             }
512         }
513     }
514 }
515
516 static int match_device(char *name, char *match)
517 {
518     char *dev;
519
520     if (name[0] == '.')
521         return 0;
522
523     /* no filter or exact match */
524     if (!match || !strcmp(name, match))
525         return 1;
526
527     /* let "01" match "xx:01" or "01" */
528     dev = strrchr(name, ':');
529     if (dev)
530         dev++;
531     else
532         dev = name;
533     if (!strcmp(dev, match))
534         return 1;
535
536     /* let "1" match "xx:01" or "01" */
537     while (*dev == '0')
538         dev++;
539     if (*dev == '\0')   /* saw "00", back up to "0" */
540         dev--;
541     if (!strcmp(dev, match))
542         return 1;
543
544     return 0;
545 }
546
547 #define SYSFS_PATH "/sys/bus/pnp/devices"
548
549 static void sysfs_dump_resources(char *name)
550 {
551     FILE *file;
552     char buf[256], *nl;
553     int first;
554     struct eisa_id *eid;
555
556     sprintf(buf, "%s/%s/resources", SYSFS_PATH, name);
557     file = fopen(buf, "r");
558     if (!file)
559         return;
560     first = 1;
561     fgets(buf, sizeof(buf), file);
562     printf("    %s", buf);      /* "state =" */
563     while (fgets(buf, sizeof(buf), file)) {
564         if (first && verbose > 1) {
565             printf("    allocated resources:\n");
566             first = 0;
567         }
568         printf("\t%s", buf);
569     }
570     fclose(file);
571
572     if (verbose < 2)
573         return;
574
575     sprintf(buf, "%s/%s/options", SYSFS_PATH, name);
576     file = fopen(buf, "r");
577     if (!file)
578         return;
579     first = 1;
580     while (fgets(buf, sizeof(buf), file)) {
581         if (first) {
582             printf("    possible resources:\n");
583             first = 0;
584         }
585         printf("\t%s", buf);
586     }
587     fclose(file);
588
589     sprintf(buf, "%s/%s/id", SYSFS_PATH, name);
590     file = fopen(buf, "r");
591     if (!file)
592         return;
593     first = 1;
594     fgets(buf, sizeof(buf), file);      /* skip first one */
595     while (fgets(buf, sizeof(buf), file)) {
596         if (first) {
597             printf("    compatible devices:\n");
598             first = 0;
599         }
600         nl = strchr(buf, '\n');
601         if (nl)
602             *nl = '\0';
603         for (eid = eisa_id; eid; eid = eid->next)
604             if (strcmp(buf, eid->id) == 0) break;
605         printf("\t%s %s\n", buf, eid ? eid->name : "(unknown)");
606     }
607     fclose(file);
608 }
609
610 static char *sysfs_get_string(char *name, char *object)
611 {
612     int fd, len;
613     char *buf, *nl;
614
615     buf = malloc(256);
616     if (!buf)
617         return 0;
618     sprintf(buf, "%s/%s/%s", SYSFS_PATH, name, object);
619     fd = open(buf, O_RDONLY);
620     if (fd < 0)
621         return 0;
622     len = read(fd, buf, 256);
623     close(fd);
624     nl = strchr(buf, '\n');
625     if (nl)
626         *nl = '\0';
627     return buf;
628 }
629
630 static int sysfs_dump_basic(char *match)
631 {
632     struct dirent **namelist;
633     char *name, *eis;
634     struct eisa_id *eid;
635     int i, n;
636
637     n = scandir(SYSFS_PATH, &namelist, 0, alphasort);
638     if (n < 0)
639         return -1;
640
641     for (i = 0; i < n; i++) {
642         name = namelist[i]->d_name;
643         if (match_device(name, match)) {
644             eis = sysfs_get_string(name, "id");
645             for (eid = eisa_id; eid; eid = eid->next)
646                 if (strcmp(eis, eid->id) == 0) break;
647             printf("%s %s %s\n", name, eis, eid ? eid->name : "(unknown)");
648             if (verbose) {
649                 sysfs_dump_resources(name);
650                 if (!match) printf("\n");
651             }
652             free(eis);
653         }
654
655         free(namelist[i]);
656     }
657     free(namelist);
658     return 0;
659 }
660
661 static int dump_basic(char *match)
662 {
663     int id, t1, t2, t3, flags;
664     struct eisa_id *eid;
665     char name[4], *eis, buf[64];
666     FILE *f;
667
668     if (sysfs_dump_basic(match) == 0)
669         return 0;
670
671     f = fopen("/proc/bus/pnp/devices", "r");
672     if (f == NULL) {
673         fprintf(stderr, "lspnp: neither %s nor /proc/bus/pnp is available\n",
674                 SYSFS_PATH);
675         return -1;
676     }
677     while (fgets(buf, 63, f) != NULL) {
678         sscanf(buf, "%2s %x %x:%x:%x %x", name, &id, &t1, &t2, &t3, &flags);
679         if (!match_device(name, match))
680             continue;
681         eis = eisa_str(id);
682         printf("%s %7s ", name, eis);
683         for (eid = eisa_id; eid; eid = eid->next)
684             if (strcmp(eis, eid->id) == 0) break;
685         if (eid)
686             printf("%s", eid->name);
687         else
688             dump_class(t1, t2);
689         printf("\n");
690         if (verbose > 1)
691             dump_flags(flags);
692         if (verbose) {
693             dump_resources(name);
694             if (!match) printf("\n");
695         }
696     }
697     fclose(f);
698     return 0;
699 }
700
701 /*====================================================================*/
702
703 void usage(char *name)
704 {
705     fprintf(stderr, "usage: %s [-b] [-v[v]] [device #]\n", name);
706     exit(EXIT_FAILURE);
707 }
708     
709 int main(int argc, char *argv[])
710 {
711     int optch, errflg = 0;
712     
713     while ((optch = getopt(argc, argv, "bv")) != -1) {
714         switch (optch) {
715         case 'b':
716             boot++; break;
717         case 'v':
718             verbose++; break;
719         default:
720             errflg = 1; break;
721         }
722     }
723     if (errflg)
724         usage(argv[0]);
725     load_ids();
726     if (optind < argc) {
727         while (optind < argc) {
728             if (dump_basic(argv[optind]) != 0)
729                 return EXIT_FAILURE;
730             optind++;
731         }
732         return EXIT_SUCCESS;
733     }
734     return dump_basic(NULL);
735 }