tests/kms_setmode: avoid 2 connectors on the same encoder
[platform/upstream/intel-gpu-tools.git] / tests / kms_setmode.c
1 /*
2  * Permission is hereby granted, free of charge, to any person obtaining a
3  * copy of this software and associated documentation files (the "Software"),
4  * to deal in the Software without restriction, including without limitation
5  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
6  * and/or sell copies of the Software, and to permit persons to whom the
7  * Software is furnished to do so, subject to the following conditions:
8  *
9  * The above copyright notice and this permission notice shall be included in
10  * all copies or substantial portions of the Software.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
17  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
18  * IN THE SOFTWARE.
19  *
20  * Authors:
21  *    Imre Deak <imre.deak@intel.com>
22  */
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <assert.h>
28 #include <cairo.h>
29 #include <errno.h>
30 #include <stdint.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <getopt.h>
34 #include <sys/time.h>
35
36 #include "drm_fourcc.h"
37 #include "drmtest.h"
38 #include "intel_bufmgr.h"
39 #include "intel_batchbuffer.h"
40 #include "intel_gpu_tools.h"
41 #include "igt_kms.h"
42
43 #define MAX_CONNECTORS  10
44 #define MAX_CRTCS       3
45
46 /* max combinations with repetitions */
47 #define MAX_COMBINATION_COUNT   \
48         (MAX_CONNECTORS * MAX_CONNECTORS * MAX_CONNECTORS)
49 #define MAX_COMBINATION_ELEMS   MAX_CRTCS
50
51 static int drm_fd;
52 static drmModeRes *drm_resources;
53 static int filter_test_id;
54 static bool dry_run;
55
56 const drmModeModeInfo mode_640_480 = {
57         .name           = "640x480",
58         .vrefresh       = 60,
59         .clock          = 25200,
60
61         .hdisplay       = 640,
62         .hsync_start    = 656,
63         .hsync_end      = 752,
64         .htotal         = 800,
65
66         .vdisplay       = 480,
67         .vsync_start    = 490,
68         .vsync_end      = 492,
69         .vtotal         = 525,
70
71         .flags          = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
72 };
73
74 enum test_flags {
75         TEST_INVALID                    = 0x01,
76         TEST_CLONE                      = 0x02,
77         TEST_SINGLE_CRTC_CLONE          = 0x04,
78         TEST_EXCLUSIVE_CRTC_CLONE       = 0x08,
79 };
80
81 struct test_config {
82         const char *name;
83         enum test_flags flags;
84         drmModeRes *resources;
85 };
86
87 struct connector_config {
88         drmModeConnector *connector;
89         int crtc_idx;
90         bool connected;
91         drmModeModeInfo default_mode;
92 };
93
94 struct crtc_config {
95         int crtc_idx;
96         int crtc_id;
97         int pipe_id;
98         int connector_count;
99         struct connector_config *cconfs;
100         struct kmstest_fb fb_info;
101         drmModeModeInfo mode;
102 };
103
104 static bool drm_mode_equal(drmModeModeInfo *m1, drmModeModeInfo *m2)
105 {
106 #define COMP(x) do { if (m1->x != m2->x) return false; } while (0)
107         COMP(vrefresh);
108         COMP(clock);
109         COMP(hdisplay);
110         COMP(hsync_start);
111         COMP(hsync_end);
112         COMP(htotal);
113         COMP(vdisplay);
114         COMP(vsync_start);
115         COMP(vsync_end);
116         COMP(vtotal);
117         COMP(flags);
118
119         return true;
120 }
121
122 static bool connector_supports_mode(drmModeConnector *connector,
123                                     drmModeModeInfo *mode)
124 {
125         int i;
126
127         for (i = 0; i < connector->count_modes; i++)
128                 if (drm_mode_equal(&connector->modes[i], mode))
129                         return true;
130
131         return false;
132 }
133
134 static bool crtc_supports_mode(struct crtc_config *crtc, drmModeModeInfo *mode)
135 {
136         int i;
137
138         for (i = 0; i < crtc->connector_count; i++) {
139                 if (!connector_supports_mode(crtc->cconfs[i].connector, mode))
140                         return false;
141         }
142
143         return true;
144 }
145
146 static int paint_fb(struct kmstest_fb *fb, const char *test_name,
147                     const char **crtc_str, int crtc_count, int current_crtc_idx)
148 {
149         double x, y;
150         cairo_t *cr;
151         int i;
152
153         cr = kmstest_get_cairo_ctx(drm_fd, fb);
154         if (!cr)
155                 return -1;
156
157         kmstest_paint_test_pattern(cr, fb->width, fb->height);
158
159         cairo_select_font_face(cr, "Helvetica", CAIRO_FONT_SLANT_NORMAL,
160                                CAIRO_FONT_WEIGHT_NORMAL);
161         cairo_move_to(cr, fb->width / 2, fb->height / 2);
162         cairo_set_font_size(cr, 24);
163         kmstest_cairo_printf_line(cr, align_hcenter, 40, "%s", test_name);
164
165         cairo_get_current_point(cr, &x, &y);
166         cairo_move_to(cr, 60, y);
167
168         for (i = 0; i < crtc_count; i++) {
169                 if (i == current_crtc_idx) {
170                         cairo_get_current_point(cr, &x, &y);
171                         cairo_move_to(cr, x - 20, y);
172                         kmstest_cairo_printf_line(cr, align_right, 20, "X");
173                         cairo_move_to(cr, x, y);
174                 }
175                 kmstest_cairo_printf_line(cr, align_left, 20, "%s",
176                                           crtc_str[i]);
177         }
178
179         return 0;
180 }
181
182 static void create_fb_for_crtc(struct crtc_config *crtc,
183                                struct kmstest_fb *fb_info)
184 {
185         int bpp;
186         int depth;
187         bool enable_tiling;
188         int fb_id;
189
190         bpp = 32;
191         depth = 24;
192         enable_tiling = false;
193         fb_id = kmstest_create_fb(drm_fd, crtc->mode.hdisplay,
194                                   crtc->mode.vdisplay, bpp, depth,
195                                   enable_tiling, fb_info);
196         assert(fb_id > 0);
197 }
198
199 static void get_mode_for_crtc(struct crtc_config *crtc,
200                               drmModeModeInfo *mode_ret)
201 {
202         drmModeModeInfo mode;
203         int i;
204
205         /*
206          * First try to select a default mode that is supported by all
207          * connectors.
208          */
209         for (i = 0; i < crtc->connector_count; i++) {
210                 mode = crtc->cconfs[i].default_mode;
211                 if (crtc_supports_mode(crtc, &mode))
212                         goto found;
213         }
214
215         /*
216          * Then just fall back to find any that is supported by all
217          * connectors.
218          */
219         for (i = 0; i < crtc->cconfs[0].connector->count_modes; i++) {
220                 mode = crtc->cconfs[0].connector->modes[i];
221                 if (crtc_supports_mode(crtc, &mode))
222                         goto found;
223         }
224
225         /*
226          * If none is found then just pick the default mode of the first
227          * connector and hope the other connectors can support it by scaling
228          * etc.
229          */
230         mode = crtc->cconfs[0].default_mode;
231 found:
232         *mode_ret = mode;
233 }
234
235 static int get_encoder_idx(drmModeRes *resources, drmModeEncoder *encoder)
236 {
237         int i;
238
239         for (i = 0; i < resources->count_encoders; i++)
240                 if (resources->encoders[i] == encoder->encoder_id)
241                         return i;
242         assert(0);
243 }
244
245 static void get_crtc_config_str(struct crtc_config *crtc, char *buf,
246                                 size_t buf_size)
247 {
248         int pos;
249         int i;
250
251         pos = snprintf(buf, buf_size,
252                        "CRTC[%d] [Pipe %s] Mode: %s@%dHz Connectors: ",
253                        crtc->crtc_id, kmstest_pipe_str(crtc->pipe_id),
254                        crtc->mode.name, crtc->mode.vrefresh);
255         if (pos > buf_size)
256                 return;
257         for (i = 0; i < crtc->connector_count; i++) {
258                 drmModeConnector *connector = crtc->cconfs[i].connector;
259
260                 pos += snprintf(&buf[pos], buf_size - pos,
261                         "%s%s-%d[%d]%s", i ? ", " : "",
262                         kmstest_connector_type_str(connector->connector_type),
263                         connector->connector_type_id, connector->connector_id,
264                         crtc->cconfs[i].connected ? "" : " (NC)");
265                 if (pos > buf_size)
266                         return;
267         }
268 }
269
270 static void setup_crtcs(drmModeRes *resources, struct connector_config *cconf,
271                         int connector_count, struct crtc_config *crtcs,
272                         int *crtc_count_ret, bool *config_valid_ret)
273 {
274         struct crtc_config *crtc;
275         int crtc_count;
276         bool config_valid;
277         int i;
278         int encoder_usage_count[resources->count_encoders];
279
280         i = 0;
281         crtc_count = 0;
282         crtc = crtcs;
283         config_valid = true;
284
285         while (i < connector_count) {
286                 drmModeCrtc *drm_crtc;
287                 unsigned long encoder_mask;
288                 int j;
289
290                 igt_assert(crtc_count < MAX_CRTCS);
291
292                 crtc->crtc_idx = cconf[i].crtc_idx;
293                 drm_crtc = drmModeGetCrtc(drm_fd,
294                                           resources->crtcs[crtc->crtc_idx]);
295                 crtc->crtc_id = drm_crtc->crtc_id;
296                 drmModeFreeCrtc(drm_crtc);
297                 crtc->pipe_id = kmstest_get_pipe_from_crtc_id(drm_fd,
298                                                               crtc->crtc_id);
299
300                 crtc->connector_count = 1;
301                 for (j = i + 1; j < connector_count; j++)
302                         if (cconf[j].crtc_idx == crtc->crtc_idx)
303                                 crtc->connector_count++;
304
305                 crtc->cconfs = malloc(sizeof(*crtc->cconfs) *
306                                       crtc->connector_count);
307                 assert(crtc->cconfs);
308
309                 encoder_mask = 0;
310                 for (j = 0; j < crtc->connector_count; j++) {
311                         drmModeConnector *connector;
312                         drmModeEncoder *encoder;
313
314                         crtc->cconfs[j] = cconf[i + j];
315                         connector = cconf[i + j].connector;
316
317                         /* Intel connectors have only a single encoder */
318                         igt_assert(connector->count_encoders == 1);
319                         encoder = drmModeGetEncoder(drm_fd,
320                                                     connector->encoders[0]);
321                         assert(encoder);
322
323                         config_valid &= !!(encoder->possible_crtcs &
324                                           (1 << crtc->crtc_idx));
325
326                         encoder_mask |= 1 << get_encoder_idx(resources,
327                                                              encoder);
328                         config_valid &= !(encoder_mask &
329                                           ~encoder->possible_clones);
330
331                         drmModeFreeEncoder(encoder);
332                 }
333                 get_mode_for_crtc(crtc, &crtc->mode);
334                 create_fb_for_crtc(crtc, &crtc->fb_info);
335
336                 i += crtc->connector_count;
337                 crtc_count++;
338                 crtc++;
339         }
340
341         memset(encoder_usage_count, 0, sizeof(encoder_usage_count));
342         for (i = 0; i < connector_count; i++) {
343                 drmModeConnector *connector = cconf[i].connector;
344                 drmModeEncoder *encoder;
345
346                 igt_assert(connector->count_encoders == 1);
347                 encoder = drmModeGetEncoder(drm_fd, connector->encoders[0]);
348                 encoder_usage_count[get_encoder_idx(resources, encoder)]++;
349                 drmModeFreeEncoder(encoder);
350         }
351         for (i = 0; i < resources->count_encoders; i++)
352                 if (encoder_usage_count[i] > 1)
353                         config_valid = false;
354
355         *crtc_count_ret = crtc_count;
356         *config_valid_ret = config_valid;
357 }
358
359 static void cleanup_crtcs(struct crtc_config *crtcs, int crtc_count)
360 {
361         int i;
362
363         for (i = 0; i < crtc_count; i++) {
364                 free(crtcs[i].cconfs);
365         }
366 }
367
368 static uint32_t *get_connector_ids(struct crtc_config *crtc)
369 {
370         uint32_t *ids;
371         int i;
372
373         ids = malloc(sizeof(*ids) * crtc->connector_count);
374         assert(ids);
375         for (i = 0; i < crtc->connector_count; i++)
376                 ids[i] = crtc->cconfs[i].connector->connector_id;
377
378         return ids;
379 }
380
381 static void test_crtc_config(const struct test_config *tconf,
382                              struct crtc_config *crtcs, int crtc_count)
383 {
384         char str_buf[MAX_CRTCS][1024];
385         const char *crtc_strs[MAX_CRTCS];
386         struct crtc_config *crtc;
387         static int test_id;
388         bool config_failed = false;
389         bool connector_connected = false;
390         int ret = 0;
391         int i;
392
393         test_id++;
394
395         if (filter_test_id && filter_test_id != test_id)
396                 return;
397
398         printf("  Test id#%d CRTC count %d\n", test_id, crtc_count);
399
400         for (i = 0; i < crtc_count; i++) {
401                 get_crtc_config_str(&crtcs[i], str_buf[i], sizeof(str_buf[i]));
402                 crtc_strs[i] = &str_buf[i][0];
403         }
404
405         if (dry_run) {
406                 for (i = 0; i < crtc_count; i++)
407                         printf("    %s\n", crtc_strs[i]);
408                 return;
409         }
410
411         for (i = 0; i < crtc_count; i++) {
412                 uint32_t *ids;
413                 int j;
414
415                 crtc = &crtcs[i];
416
417                 printf("    %s\n", crtc_strs[i]);
418
419                 create_fb_for_crtc(crtc, &crtc->fb_info);
420                 paint_fb(&crtc->fb_info, tconf->name, crtc_strs, crtc_count, i);
421
422                 ids = get_connector_ids(crtc);
423                 ret = drmModeSetCrtc(drm_fd, crtc->crtc_id,
424                                      crtc->fb_info.fb_id, 0, 0, ids,
425                                      crtc->connector_count, &crtc->mode);
426                 free(ids);
427
428                 if (ret < 0) {
429                         igt_assert(errno == EINVAL);
430                         config_failed = true;
431                 }
432
433                 for (j = 0; j < crtc->connector_count; j++)
434                         connector_connected |= crtc->cconfs[j].connected;
435         }
436
437         igt_assert(config_failed == !!(tconf->flags & TEST_INVALID));
438
439         if (ret == 0 && connector_connected && !(tconf->flags & TEST_INVALID))
440                 sleep(5);
441
442         for (i = 0; i < crtc_count; i++) {
443                 if (crtcs[i].fb_info.fb_id) {
444                         drmModeSetCrtc(drm_fd, crtcs[i].crtc_id, 0, 0, 0, NULL,
445                                         0, NULL);
446                         drmModeRmFB(drm_fd, crtcs[i].fb_info.fb_id);
447                         crtcs[i].fb_info.fb_id = 0;
448                 }
449         }
450
451         return;
452 }
453
454 static void test_one_combination(const struct test_config *tconf,
455                                  struct connector_config *cconfs,
456                                  int connector_count)
457 {
458         struct crtc_config crtcs[MAX_CRTCS];
459         int crtc_count;
460         bool config_valid;
461
462         setup_crtcs(tconf->resources, cconfs, connector_count, crtcs,
463                     &crtc_count, &config_valid);
464
465         if (config_valid == !(tconf->flags & TEST_INVALID))
466                 test_crtc_config(tconf, crtcs, crtc_count);
467
468         cleanup_crtcs(crtcs, crtc_count);
469 }
470
471 static int assign_crtc_to_connectors(const struct test_config *tconf,
472                                      int *crtc_idxs, int connector_count,
473                                      struct connector_config *cconfs)
474 {
475         unsigned long crtc_idx_mask;
476         int i;
477
478         crtc_idx_mask = 0;
479         for (i = 0; i < connector_count; i++) {
480                 int crtc_idx = crtc_idxs[i];
481
482                 if ((tconf->flags & TEST_SINGLE_CRTC_CLONE) &&
483                     crtc_idx_mask & ~(1 << crtc_idx))
484                         return -1;
485
486                 if ((tconf->flags & TEST_EXCLUSIVE_CRTC_CLONE) &&
487                     crtc_idx_mask & (1 << crtc_idx))
488                         return -1;
489
490                 crtc_idx_mask |= 1 << crtc_idx;
491
492                 cconfs[i].crtc_idx = crtc_idx;
493         }
494
495         return 0;
496 }
497
498 static int get_one_connector(drmModeRes *resources, int connector_id,
499                              struct connector_config *cconf)
500 {
501         drmModeConnector *connector;
502         drmModeModeInfo mode;
503
504         connector = drmModeGetConnector(drm_fd, connector_id);
505         assert(connector);
506         cconf->connector = connector;
507
508         cconf->connected = connector->connection == DRM_MODE_CONNECTED;
509
510         /*
511          * For DP/eDP we need a connected sink, since mode setting depends
512          * on successful link training and retrieved DPCD parameters.
513          */
514         switch (connector->connector_type) {
515         case DRM_MODE_CONNECTOR_DisplayPort:
516         case DRM_MODE_CONNECTOR_eDP:
517                 if (!cconf->connected) {
518                         drmModeFreeConnector(connector);
519                         return -1;
520                 }
521         }
522
523         if (cconf->connected) {
524                 if (kmstest_get_connector_default_mode(drm_fd, connector,
525                                                         &mode) < 0)
526                         mode = mode_640_480;
527         } else {
528                 mode = mode_640_480;
529         }
530
531         cconf->default_mode = mode;
532
533         return 0;
534 }
535
536 static int get_connectors(drmModeRes *resources, int *connector_idxs,
537                           int connector_count, struct connector_config *cconfs)
538 {
539         int i;
540
541         for (i = 0; i < connector_count; i++) {
542                 int connector_idx;
543                 int connector_id;
544
545                 connector_idx = connector_idxs[i];
546                 assert(connector_idx < resources->count_connectors);
547                 connector_id = resources->connectors[connector_idx];
548
549                 if (get_one_connector(resources, connector_id, &cconfs[i]) < 0)
550                         goto err;
551
552         }
553
554         return 0;
555
556 err:
557         while (i--)
558                 drmModeFreeConnector(cconfs[i].connector);
559
560         return -1;
561 }
562
563 static void free_connectors(struct connector_config *cconfs,
564                             int connector_count)
565 {
566         int i;
567
568         for (i = 0; i < connector_count; i++)
569                 drmModeFreeConnector(cconfs[i].connector);
570 }
571
572 struct combination {
573         int elems[MAX_COMBINATION_ELEMS];
574 };
575
576 struct combination_set {
577         int count;
578         struct combination items[MAX_COMBINATION_COUNT];
579 };
580
581 /*
582  * Get all possible selection of k elements from n elements with or without
583  * repetitions.
584  */
585 static void iterate_combinations(int n, int k, bool allow_repetitions,
586                                  int depth, int base, struct combination *comb,
587                                  struct combination_set *set)
588 {
589         int v;
590
591         if (!k) {
592                 assert(set->count < ARRAY_SIZE(set->items));
593                 set->items[set->count++] = *comb;
594                 return;
595         }
596
597         for (v = base; v < n; v++) {
598                 comb->elems[depth] = v;
599                 iterate_combinations(n, k - 1, allow_repetitions,
600                                      depth + 1, allow_repetitions ? 0 : v + 1,
601                                      comb, set);
602         }
603
604 }
605
606 static void get_combinations(int n, int k, bool allow_repetitions,
607                              struct combination_set *set)
608 {
609         struct combination comb;
610
611         assert(k <= ARRAY_SIZE(set->items[0].elems));
612         set->count = 0;
613         iterate_combinations(n, k, allow_repetitions, 0, 0, &comb, set);
614 }
615
616 static void test_combinations(const struct test_config *tconf,
617                               int connector_count)
618 {
619         struct combination_set connector_combs;
620         struct combination_set crtc_combs;
621         struct connector_config *cconfs;
622         int i;
623
624         get_combinations(tconf->resources->count_connectors, connector_count,
625                          false, &connector_combs);
626         get_combinations(tconf->resources->count_crtcs, connector_count,
627                          true, &crtc_combs);
628
629         printf("Testing: %s %d connector combinations\n", tconf->name,
630                 connector_count);
631         for (i = 0; i < connector_combs.count; i++) {
632                 int *connector_idxs;
633                 int ret;
634                 int j;
635
636                 cconfs = malloc(sizeof(*cconfs) * connector_count);
637                 assert(cconfs);
638
639                 connector_idxs = &connector_combs.items[i].elems[0];
640                 ret = get_connectors(tconf->resources, connector_idxs,
641                                      connector_count, cconfs);
642                 if (ret < 0)
643                         goto free_cconfs;
644
645                 for (j = 0; j < crtc_combs.count; j++) {
646                         int *crtc_idxs = &crtc_combs.items[j].elems[0];
647                         ret = assign_crtc_to_connectors(tconf, crtc_idxs,
648                                                         connector_count,
649                                                         cconfs);
650                         if (ret < 0)
651                                 continue;
652
653                         test_one_combination(tconf, cconfs, connector_count);
654                 }
655
656                 free_connectors(cconfs, connector_count);
657 free_cconfs:
658                 free(cconfs);
659         }
660 }
661
662 static void run_test(const struct test_config *tconf)
663 {
664         int connector_num;
665
666         connector_num = tconf->flags & TEST_CLONE ? 2 : 1;
667         for (; connector_num <= tconf->resources->count_crtcs; connector_num++)
668                 test_combinations(tconf, connector_num);
669 }
670
671 static int opt_handler(int opt, int opt_index)
672 {
673         switch (opt) {
674         case 'd':
675                 dry_run = true;
676                 break;
677         case 't':
678                 filter_test_id = atoi(optarg);
679                 break;
680         default:
681                 assert(0);
682         }
683
684         return 0;
685 }
686
687 int main(int argc, char **argv)
688 {
689         const struct {
690                 enum test_flags flags;
691                 const char *name;
692         } tests[] = {
693                 { TEST_INVALID | TEST_CLONE | TEST_SINGLE_CRTC_CLONE,
694                                         "invalid-clone-single-crtc" },
695                 { TEST_INVALID | TEST_CLONE | TEST_EXCLUSIVE_CRTC_CLONE,
696                                         "invalid-clone-exclusive-crtc" },
697                 { TEST_CLONE | TEST_EXCLUSIVE_CRTC_CLONE,
698                                         "clone-exclusive-crtc" },
699         };
700         const char *help_str =
701                "  -d\t\tDon't run any test, only print what would be done. (still needs DRM access)\n"
702                "  -t <test id>\tRun only the test with this id.";
703         int i;
704         int ret;
705
706         ret = igt_subtest_init_parse_opts(argc, argv, "dt:", NULL, help_str,
707                                           opt_handler);
708         if (ret < 0)
709                 return ret == -1 ? 0 : ret;
710
711         igt_skip_on_simulation();
712
713         if (dry_run && filter_test_id) {
714                 fprintf(stderr, "only one of -d and -t is accepted\n");
715                 exit(1);
716         }
717
718         igt_fixture {
719                 drm_fd = drm_open_any();
720                 if (!dry_run)
721                         igt_set_vt_graphics_mode();
722
723                 drm_resources = drmModeGetResources(drm_fd);
724                 assert(drm_resources);
725         }
726
727         for (i = 0; i < ARRAY_SIZE(tests); i++) {
728                 igt_subtest(tests[i].name) {
729                         struct test_config tconf = {
730                                 .flags          = tests[i].flags,
731                                 .name           = tests[i].name,
732                                 .resources      = drm_resources,
733                         };
734                         run_test(&tconf);
735                 }
736         }
737
738         igt_fixture {
739                 drmModeFreeResources(drm_resources);
740
741                 close(drm_fd);
742         }
743
744         igt_exit();
745 }