Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4358 | Serge | 1 | /* |
2 | * Copyright 2011-2013 Maarten Lankhorst |
||
3 | * |
||
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
||
5 | * copy of this software and associated documentation files (the "Software"), |
||
6 | * to deal in the Software without restriction, including without limitation |
||
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||
8 | * and/or sell copies of the Software, and to permit persons to whom the |
||
9 | * Software is furnished to do so, subject to the following conditions: |
||
10 | * |
||
11 | * The above copyright notice and this permission notice shall be included in |
||
12 | * all copies or substantial portions of the Software. |
||
13 | * |
||
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR |
||
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
||
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
||
20 | * OTHER DEALINGS IN THE SOFTWARE. |
||
21 | */ |
||
22 | |||
23 | #include "nvc0_video.h" |
||
24 | |||
25 | struct strparm_bsp { |
||
26 | uint32_t w0[4]; // bits 0-23 length, bits 24-31 addr_hi |
||
27 | uint32_t w1[4]; // bit 8-24 addr_lo |
||
28 | uint32_t unk20; // should be idx * 0x8000000, bitstream offset |
||
29 | uint32_t do_crypto_crap; // set to 0 |
||
30 | }; |
||
31 | |||
32 | struct mpeg12_picparm_bsp { |
||
33 | uint16_t width; |
||
34 | uint16_t height; |
||
35 | uint8_t picture_structure; |
||
36 | uint8_t picture_coding_type; |
||
37 | uint8_t intra_dc_precision; |
||
38 | uint8_t frame_pred_frame_dct; |
||
39 | uint8_t concealment_motion_vectors; |
||
40 | uint8_t intra_vlc_format; |
||
41 | uint16_t pad; |
||
42 | uint8_t f_code[2][2]; |
||
43 | }; |
||
44 | |||
45 | struct mpeg4_picparm_bsp { |
||
46 | uint16_t width; |
||
47 | uint16_t height; |
||
48 | uint8_t vop_time_increment_size; |
||
49 | uint8_t interlaced; |
||
50 | uint8_t resync_marker_disable; |
||
51 | }; |
||
52 | |||
53 | struct vc1_picparm_bsp { |
||
54 | uint16_t width; |
||
55 | uint16_t height; |
||
56 | uint8_t profile; // 04 0 simple, 1 main, 2 advanced |
||
57 | uint8_t postprocflag; // 05 |
||
58 | uint8_t pulldown; // 06 |
||
59 | uint8_t interlaced; // 07 |
||
60 | uint8_t tfcntrflag; // 08 |
||
61 | uint8_t finterpflag; // 09 |
||
62 | uint8_t psf; // 0a |
||
63 | uint8_t pad; // 0b |
||
64 | uint8_t multires; // 0c |
||
65 | uint8_t syncmarker; // 0d |
||
66 | uint8_t rangered; // 0e |
||
67 | uint8_t maxbframes; // 0f |
||
68 | uint8_t dquant; // 10 |
||
69 | uint8_t panscan_flag; // 11 |
||
70 | uint8_t refdist_flag; // 12 |
||
71 | uint8_t quantizer; // 13 |
||
72 | uint8_t extended_mv; // 14 |
||
73 | uint8_t extended_dmv; // 15 |
||
74 | uint8_t overlap; // 16 |
||
75 | uint8_t vstransform; // 17 |
||
76 | }; |
||
77 | |||
78 | struct h264_picparm_bsp { |
||
79 | // 00 |
||
80 | uint32_t unk00; |
||
81 | // 04 |
||
82 | uint32_t log2_max_frame_num_minus4; // 04 checked |
||
83 | uint32_t pic_order_cnt_type; // 08 checked |
||
84 | uint32_t log2_max_pic_order_cnt_lsb_minus4; // 0c checked |
||
85 | uint32_t delta_pic_order_always_zero_flag; // 10, or unknown |
||
86 | |||
87 | uint32_t frame_mbs_only_flag; // 14, always 1? |
||
88 | uint32_t direct_8x8_inference_flag; // 18, always 1? |
||
89 | uint32_t width_mb; // 1c checked |
||
90 | uint32_t height_mb; // 20 checked |
||
91 | // 24 |
||
92 | //struct picparm2 |
||
93 | uint32_t entropy_coding_mode_flag; // 00, checked |
||
94 | uint32_t pic_order_present_flag; // 04 checked |
||
95 | uint32_t unk; // 08 seems to be 0? |
||
96 | uint32_t pad1; // 0c seems to be 0? |
||
97 | uint32_t pad2; // 10 always 0 ? |
||
98 | uint32_t num_ref_idx_l0_active_minus1; // 14 always 0? |
||
99 | uint32_t num_ref_idx_l1_active_minus1; // 18 always 0? |
||
100 | uint32_t weighted_pred_flag; // 1c checked |
||
101 | uint32_t weighted_bipred_idc; // 20 checked |
||
102 | uint32_t pic_init_qp_minus26; // 24 checked |
||
103 | uint32_t deblocking_filter_control_present_flag; // 28 always 1? |
||
104 | uint32_t redundant_pic_cnt_present_flag; // 2c always 0? |
||
105 | uint32_t transform_8x8_mode_flag; // 30 checked |
||
106 | uint32_t mb_adaptive_frame_field_flag; // 34 checked-ish |
||
107 | uint8_t field_pic_flag; // 38 checked |
||
108 | uint8_t bottom_field_flag; // 39 checked |
||
109 | uint8_t real_pad[0x1b]; // XX why? |
||
110 | }; |
||
111 | |||
112 | static uint32_t |
||
113 | nvc0_decoder_fill_picparm_mpeg12_bsp(struct nvc0_decoder *dec, |
||
114 | struct pipe_mpeg12_picture_desc *desc, |
||
115 | char *map) |
||
116 | { |
||
117 | struct mpeg12_picparm_bsp *pic_bsp = (struct mpeg12_picparm_bsp *)map; |
||
118 | int i; |
||
119 | pic_bsp->width = dec->base.width; |
||
120 | pic_bsp->height = dec->base.height; |
||
121 | pic_bsp->picture_structure = desc->picture_structure; |
||
122 | pic_bsp->picture_coding_type = desc->picture_coding_type; |
||
123 | pic_bsp->intra_dc_precision = desc->intra_dc_precision; |
||
124 | pic_bsp->frame_pred_frame_dct = desc->frame_pred_frame_dct; |
||
125 | pic_bsp->concealment_motion_vectors = desc->concealment_motion_vectors; |
||
126 | pic_bsp->intra_vlc_format = desc->intra_vlc_format; |
||
127 | pic_bsp->pad = 0; |
||
128 | for (i = 0; i < 4; ++i) |
||
129 | pic_bsp->f_code[i/2][i%2] = desc->f_code[i/2][i%2] + 1; // FU |
||
130 | |||
131 | return (desc->num_slices << 4) | (dec->base.profile != PIPE_VIDEO_PROFILE_MPEG1); |
||
132 | } |
||
133 | |||
134 | static uint32_t |
||
135 | nvc0_decoder_fill_picparm_mpeg4_bsp(struct nvc0_decoder *dec, |
||
136 | struct pipe_mpeg4_picture_desc *desc, |
||
137 | char *map) |
||
138 | { |
||
139 | struct mpeg4_picparm_bsp *pic_bsp = (struct mpeg4_picparm_bsp *)map; |
||
140 | uint32_t t, bits = 0; |
||
141 | pic_bsp->width = dec->base.width; |
||
142 | pic_bsp->height = dec->base.height; |
||
143 | assert(desc->vop_time_increment_resolution > 0); |
||
144 | |||
145 | t = desc->vop_time_increment_resolution - 1; |
||
146 | while (t) { |
||
147 | bits++; |
||
148 | t /= 2; |
||
149 | } |
||
150 | if (!bits) |
||
151 | bits = 1; |
||
152 | t = desc->vop_time_increment_resolution - 1; |
||
153 | pic_bsp->vop_time_increment_size = bits; |
||
154 | pic_bsp->interlaced = desc->interlaced; |
||
155 | pic_bsp->resync_marker_disable = desc->resync_marker_disable; |
||
156 | return 4; |
||
157 | } |
||
158 | |||
159 | static uint32_t |
||
160 | nvc0_decoder_fill_picparm_vc1_bsp(struct nvc0_decoder *dec, |
||
161 | struct pipe_vc1_picture_desc *d, |
||
162 | char *map) |
||
163 | { |
||
164 | struct vc1_picparm_bsp *vc = (struct vc1_picparm_bsp *)map; |
||
165 | uint32_t caps = (d->slice_count << 4)&0xfff0; |
||
166 | vc->width = dec->base.width; |
||
167 | vc->height = dec->base.height; |
||
168 | vc->profile = dec->base.profile - PIPE_VIDEO_PROFILE_VC1_SIMPLE; // 04 |
||
169 | vc->postprocflag = d->postprocflag; |
||
170 | vc->pulldown = d->pulldown; |
||
171 | vc->interlaced = d->interlace; |
||
172 | vc->tfcntrflag = d->tfcntrflag; // 08 |
||
173 | vc->finterpflag = d->finterpflag; |
||
174 | vc->psf = d->psf; |
||
175 | vc->pad = 0; |
||
176 | vc->multires = d->multires; // 0c |
||
177 | vc->syncmarker = d->syncmarker; |
||
178 | vc->rangered = d->rangered; |
||
179 | vc->maxbframes = d->maxbframes; |
||
180 | vc->dquant = d->dquant; // 10 |
||
181 | vc->panscan_flag = d->panscan_flag; |
||
182 | vc->refdist_flag = d->refdist_flag; |
||
183 | vc->quantizer = d->quantizer; |
||
184 | vc->extended_mv = d->extended_mv; // 14 |
||
185 | vc->extended_dmv = d->extended_dmv; |
||
186 | vc->overlap = d->overlap; |
||
187 | vc->vstransform = d->vstransform; |
||
188 | return caps | 2; |
||
189 | } |
||
190 | |||
191 | static uint32_t |
||
192 | nvc0_decoder_fill_picparm_h264_bsp(struct nvc0_decoder *dec, |
||
193 | struct pipe_h264_picture_desc *d, |
||
194 | char *map) |
||
195 | { |
||
196 | struct h264_picparm_bsp stub_h = {}, *h = &stub_h; |
||
197 | uint32_t caps = (d->slice_count << 4)&0xfff0; |
||
198 | |||
199 | assert(!(d->slice_count & ~0xfff)); |
||
200 | if (d->slice_count & 0x1000) |
||
201 | caps |= 1 << 20; |
||
202 | |||
203 | assert(offsetof(struct h264_picparm_bsp, bottom_field_flag) == (0x39 + 0x24)); |
||
204 | h->unk00 = 1; |
||
205 | h->pad1 = h->pad2 = 0; |
||
206 | h->unk = 0; |
||
207 | h->log2_max_frame_num_minus4 = d->log2_max_frame_num_minus4; |
||
208 | h->frame_mbs_only_flag = d->frame_mbs_only_flag; |
||
209 | h->direct_8x8_inference_flag = d->direct_8x8_inference_flag; |
||
210 | h->width_mb = mb(dec->base.width); |
||
211 | h->height_mb = mb(dec->base.height); |
||
212 | h->entropy_coding_mode_flag = d->entropy_coding_mode_flag; |
||
213 | h->pic_order_present_flag = d->pic_order_present_flag; |
||
214 | h->pic_order_cnt_type = d->pic_order_cnt_type; |
||
215 | h->log2_max_pic_order_cnt_lsb_minus4 = d->log2_max_pic_order_cnt_lsb_minus4; |
||
216 | h->delta_pic_order_always_zero_flag = d->delta_pic_order_always_zero_flag; |
||
217 | h->num_ref_idx_l0_active_minus1 = d->num_ref_idx_l0_active_minus1; |
||
218 | h->num_ref_idx_l1_active_minus1 = d->num_ref_idx_l1_active_minus1; |
||
219 | h->weighted_pred_flag = d->weighted_pred_flag; |
||
220 | h->weighted_bipred_idc = d->weighted_bipred_idc; |
||
221 | h->pic_init_qp_minus26 = d->pic_init_qp_minus26; |
||
222 | h->deblocking_filter_control_present_flag = d->deblocking_filter_control_present_flag; |
||
223 | h->redundant_pic_cnt_present_flag = d->redundant_pic_cnt_present_flag; |
||
224 | h->transform_8x8_mode_flag = d->transform_8x8_mode_flag; |
||
225 | h->mb_adaptive_frame_field_flag = d->mb_adaptive_frame_field_flag; |
||
226 | h->field_pic_flag = d->field_pic_flag; |
||
227 | h->bottom_field_flag = d->bottom_field_flag; |
||
228 | memset(h->real_pad, 0, sizeof(h->real_pad)); |
||
229 | *(struct h264_picparm_bsp *)map = *h; |
||
230 | return caps | 3; |
||
231 | } |
||
232 | |||
233 | #if NVC0_DEBUG_FENCE |
||
234 | static void dump_comm_bsp(struct comm *comm) |
||
235 | { |
||
236 | unsigned idx = comm->bsp_cur_index & 0xf; |
||
237 | debug_printf("Cur seq: %x, bsp byte ofs: %x\n", comm->bsp_cur_index, comm->byte_ofs); |
||
238 | debug_printf("Status: %08x, pos: %08x\n", comm->status[idx], comm->pos[idx]); |
||
239 | } |
||
240 | #endif |
||
241 | |||
242 | unsigned |
||
243 | nvc0_decoder_bsp(struct nvc0_decoder *dec, union pipe_desc desc, |
||
244 | struct nvc0_video_buffer *target, |
||
245 | unsigned comm_seq, unsigned num_buffers, |
||
246 | const void *const *data, const unsigned *num_bytes, |
||
247 | unsigned *vp_caps, unsigned *is_ref, |
||
248 | struct nvc0_video_buffer *refs[16]) |
||
249 | { |
||
250 | struct nouveau_pushbuf *push = dec->pushbuf[0]; |
||
251 | enum pipe_video_codec codec = u_reduce_video_profile(dec->base.profile); |
||
252 | char *bsp; |
||
253 | uint32_t bsp_addr, comm_addr, inter_addr; |
||
254 | uint32_t slice_size, bucket_size, ring_size; |
||
255 | uint32_t endmarker, caps; |
||
256 | struct strparm_bsp *str_bsp; |
||
257 | int ret, i; |
||
258 | struct nouveau_bo *bsp_bo = dec->bsp_bo[comm_seq % NVC0_VIDEO_QDEPTH]; |
||
259 | struct nouveau_bo *inter_bo = dec->inter_bo[comm_seq & 1]; |
||
260 | unsigned fence_extra = 0; |
||
261 | struct nouveau_pushbuf_refn bo_refs[] = { |
||
262 | { bsp_bo, NOUVEAU_BO_RD | NOUVEAU_BO_VRAM }, |
||
263 | { inter_bo, NOUVEAU_BO_WR | NOUVEAU_BO_VRAM }, |
||
264 | #ifdef NVC0_DEBUG_FENCE |
||
265 | { dec->fence_bo, NOUVEAU_BO_WR | NOUVEAU_BO_GART }, |
||
266 | #endif |
||
267 | { dec->bitplane_bo, NOUVEAU_BO_RDWR | NOUVEAU_BO_VRAM }, |
||
268 | }; |
||
269 | int num_refs = sizeof(bo_refs)/sizeof(*bo_refs); |
||
270 | |||
271 | if (!dec->bitplane_bo) |
||
272 | num_refs--; |
||
273 | |||
274 | #ifdef NVC0_DEBUG_FENCE |
||
275 | fence_extra = 4; |
||
276 | #endif |
||
277 | |||
278 | ret = nouveau_bo_map(bsp_bo, NOUVEAU_BO_WR, dec->client); |
||
279 | if (ret) { |
||
280 | debug_printf("map failed: %i %s\n", ret, strerror(-ret)); |
||
281 | return -1; |
||
282 | } |
||
283 | bsp = bsp_bo->map; |
||
284 | /* |
||
285 | * 0x000..0x100: picparm_bsp |
||
286 | * 0x200..0x500: picparm_vp |
||
287 | * 0x500..0x700: comm |
||
288 | * 0x700..onward: raw bitstream |
||
289 | */ |
||
290 | |||
291 | switch (codec){ |
||
292 | case PIPE_VIDEO_CODEC_MPEG12: |
||
293 | endmarker = 0xb7010000; |
||
294 | caps = nvc0_decoder_fill_picparm_mpeg12_bsp(dec, desc.mpeg12, bsp); |
||
295 | break; |
||
296 | case PIPE_VIDEO_CODEC_MPEG4: |
||
297 | endmarker = 0xb1010000; |
||
298 | caps = nvc0_decoder_fill_picparm_mpeg4_bsp(dec, desc.mpeg4, bsp); |
||
299 | break; |
||
300 | case PIPE_VIDEO_CODEC_VC1: { |
||
301 | endmarker = 0x0a010000; |
||
302 | caps = nvc0_decoder_fill_picparm_vc1_bsp(dec, desc.vc1, bsp); |
||
303 | break; |
||
304 | } |
||
305 | case PIPE_VIDEO_CODEC_MPEG4_AVC: { |
||
306 | endmarker = 0x0b010000; |
||
307 | caps = nvc0_decoder_fill_picparm_h264_bsp(dec, desc.h264, bsp); |
||
308 | break; |
||
309 | } |
||
310 | default: assert(0); return -1; |
||
311 | } |
||
312 | |||
313 | nvc0_decoder_vp_caps(dec, desc, target, comm_seq, vp_caps, is_ref, refs); |
||
314 | |||
315 | nouveau_pushbuf_space(push, 6 + (codec == PIPE_VIDEO_CODEC_MPEG4_AVC ? 9 : 7) + fence_extra + 2, num_refs, 0); |
||
316 | nouveau_pushbuf_refn(push, bo_refs, num_refs); |
||
317 | |||
318 | caps |= 0 << 16; // reset struct comm if flag is set |
||
319 | caps |= 1 << 17; // enable watchdog |
||
320 | caps |= 0 << 18; // do not report error to VP, so it can continue decoding what we have |
||
321 | caps |= 0 << 19; // if enabled, use crypto crap? |
||
322 | bsp += 0x100; |
||
323 | |||
324 | str_bsp = (struct strparm_bsp *)bsp; |
||
325 | memset(str_bsp, 0, 0x80); |
||
326 | str_bsp->w0[0] = 16; |
||
327 | str_bsp->w1[0] = 0x1; |
||
328 | bsp += 0x100; |
||
329 | /* Reserved for picparm_vp */ |
||
330 | bsp += 0x300; |
||
331 | /* Reserved for comm */ |
||
332 | #if !NVC0_DEBUG_FENCE |
||
333 | memset(bsp, 0, 0x200); |
||
334 | #endif |
||
335 | bsp += 0x200; |
||
336 | for (i = 0; i < num_buffers; ++i) { |
||
337 | memcpy(bsp, data[i], num_bytes[i]); |
||
338 | bsp += num_bytes[i]; |
||
339 | str_bsp->w0[0] += num_bytes[i]; |
||
340 | } |
||
341 | |||
342 | /* Append end sequence */ |
||
343 | *(uint32_t *)bsp = endmarker; |
||
344 | bsp += 4; |
||
345 | *(uint32_t *)bsp = 0x00000000; |
||
346 | bsp += 4; |
||
347 | *(uint32_t *)bsp = endmarker; |
||
348 | bsp += 4; |
||
349 | *(uint32_t *)bsp = 0x00000000; |
||
350 | |||
351 | bsp_addr = bsp_bo->offset >> 8; |
||
352 | inter_addr = inter_bo->offset >> 8; |
||
353 | |||
354 | #if NVC0_DEBUG_FENCE |
||
355 | memset(dec->comm, 0, 0x200); |
||
356 | comm_addr = (dec->fence_bo->offset + COMM_OFFSET) >> 8; |
||
357 | #else |
||
358 | comm_addr = bsp_addr + (COMM_OFFSET>>8); |
||
359 | #endif |
||
360 | |||
361 | BEGIN_NVC0(push, SUBC_BSP(0x700), 5); |
||
362 | PUSH_DATA (push, caps); // 700 cmd |
||
363 | PUSH_DATA (push, bsp_addr + 1); // 704 strparm_bsp |
||
364 | PUSH_DATA (push, bsp_addr + 7); // 708 str addr |
||
365 | PUSH_DATA (push, comm_addr); // 70c comm |
||
366 | PUSH_DATA (push, comm_seq); // 710 seq |
||
367 | |||
368 | if (codec != PIPE_VIDEO_CODEC_MPEG4_AVC) { |
||
369 | u32 bitplane_addr; |
||
370 | |||
371 | bitplane_addr = dec->bitplane_bo->offset >> 8; |
||
372 | |||
373 | nvc0_decoder_inter_sizes(dec, 1, &slice_size, &bucket_size, &ring_size); |
||
374 | BEGIN_NVC0(push, SUBC_BSP(0x400), 6); |
||
375 | PUSH_DATA (push, bsp_addr); // 400 picparm addr |
||
376 | PUSH_DATA (push, inter_addr); // 404 interparm addr |
||
377 | PUSH_DATA (push, inter_addr + slice_size + bucket_size); // 408 interdata addr |
||
378 | PUSH_DATA (push, ring_size << 8); // 40c interdata_size |
||
379 | PUSH_DATA (push, bitplane_addr); // 410 BITPLANE_DATA |
||
380 | PUSH_DATA (push, 0x400); // 414 BITPLANE_DATA_SIZE |
||
381 | } else { |
||
382 | nvc0_decoder_inter_sizes(dec, desc.h264->slice_count, &slice_size, &bucket_size, &ring_size); |
||
383 | BEGIN_NVC0(push, SUBC_BSP(0x400), 8); |
||
384 | PUSH_DATA (push, bsp_addr); // 400 picparm addr |
||
385 | PUSH_DATA (push, inter_addr); // 404 interparm addr |
||
386 | PUSH_DATA (push, slice_size << 8); // 408 interparm size? |
||
387 | PUSH_DATA (push, inter_addr + slice_size + bucket_size); // 40c interdata addr |
||
388 | PUSH_DATA (push, ring_size << 8); // 410 interdata size |
||
389 | PUSH_DATA (push, inter_addr + slice_size); // 414 bucket? |
||
390 | PUSH_DATA (push, bucket_size << 8); // 418 bucket size? unshifted.. |
||
391 | PUSH_DATA (push, 0); // 41c targets |
||
392 | // TODO: Double check 414 / 418 with nvidia trace |
||
393 | } |
||
394 | |||
395 | #if NVC0_DEBUG_FENCE |
||
396 | BEGIN_NVC0(push, SUBC_BSP(0x240), 3); |
||
397 | PUSH_DATAh(push, dec->fence_bo->offset); |
||
398 | PUSH_DATA (push, dec->fence_bo->offset); |
||
399 | PUSH_DATA (push, dec->fence_seq); |
||
400 | |||
401 | BEGIN_NVC0(push, SUBC_BSP(0x300), 1); |
||
402 | PUSH_DATA (push, 1); |
||
403 | PUSH_KICK (push); |
||
404 | |||
405 | { |
||
406 | unsigned spin = 0; |
||
407 | do { |
||
408 | usleep(100); |
||
409 | if ((spin++ & 0xff) == 0xff) { |
||
410 | debug_printf("b%u: %u\n", dec->fence_seq, dec->fence_map[0]); |
||
411 | dump_comm_bsp(dec->comm); |
||
412 | } |
||
413 | } while (dec->fence_seq > dec->fence_map[0]); |
||
414 | } |
||
415 | |||
416 | dump_comm_bsp(dec->comm); |
||
417 | return dec->comm->status[comm_seq & 0xf]; |
||
418 | #else |
||
419 | BEGIN_NVC0(push, SUBC_BSP(0x300), 1); |
||
420 | PUSH_DATA (push, 0); |
||
421 | PUSH_KICK (push); |
||
422 | return 2; |
||
423 | #endif |
||
424 | }><>><>><>><>>><>><>><>><>><>><>><>><>> |