diff options
author | Hans Verkuil <hans.verkuil@cisco.com> | 2013-12-13 11:13:41 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <m.chehab@samsung.com> | 2014-01-07 04:10:41 -0500 |
commit | b2f2f04719638322b9d792ecd25f70a55acf8270 (patch) | |
tree | a2f7091a72397315b41aced3774092902b256df3 | |
parent | 63faabfd89f4db9862ae2a663193e511419c67eb (diff) |
[media] vb2: remove the 'fileio = NULL' hack
The read/write implementation in vb2 reuses existing vb2 functions, but
it sets q->fileio to NULL before calling them in order to skip the
'q->fileio != NULL' check.
This works today due to the synchronous nature of read/write, but it
1) is ugly, and 2) will fail in an asynchronous use-case such as a
thread queuing and dequeuing buffers. This last example will be necessary
in order to implement vb2 DVB support.
This patch removes the hack by splitting up the dqbuf/qbuf/streamon/streamoff
functions into an external and an internal version. The external version
checks q->fileio and then calls the internal version. The read/write
implementation now just uses the internal version, removing the need to
set q->fileio to NULL.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
-rw-r--r-- | drivers/media/v4l2-core/videobuf2-core.c | 223 |
1 files changed, 112 insertions, 111 deletions
diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index a9a9c6a1c158..14a360490be9 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c | |||
@@ -1307,11 +1307,6 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) | |||
1307 | static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, | 1307 | static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, |
1308 | const char *opname) | 1308 | const char *opname) |
1309 | { | 1309 | { |
1310 | if (q->fileio) { | ||
1311 | dprintk(1, "%s(): file io in progress\n", opname); | ||
1312 | return -EBUSY; | ||
1313 | } | ||
1314 | |||
1315 | if (b->type != q->type) { | 1310 | if (b->type != q->type) { |
1316 | dprintk(1, "%s(): invalid buffer type\n", opname); | 1311 | dprintk(1, "%s(): invalid buffer type\n", opname); |
1317 | return -EINVAL; | 1312 | return -EINVAL; |
@@ -1353,9 +1348,15 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, | |||
1353 | */ | 1348 | */ |
1354 | int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) | 1349 | int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) |
1355 | { | 1350 | { |
1356 | int ret = vb2_queue_or_prepare_buf(q, b, "prepare_buf"); | ||
1357 | struct vb2_buffer *vb; | 1351 | struct vb2_buffer *vb; |
1352 | int ret; | ||
1353 | |||
1354 | if (q->fileio) { | ||
1355 | dprintk(1, "%s(): file io in progress\n", __func__); | ||
1356 | return -EBUSY; | ||
1357 | } | ||
1358 | 1358 | ||
1359 | ret = vb2_queue_or_prepare_buf(q, b, "prepare_buf"); | ||
1359 | if (ret) | 1360 | if (ret) |
1360 | return ret; | 1361 | return ret; |
1361 | 1362 | ||
@@ -1377,24 +1378,7 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) | |||
1377 | } | 1378 | } |
1378 | EXPORT_SYMBOL_GPL(vb2_prepare_buf); | 1379 | EXPORT_SYMBOL_GPL(vb2_prepare_buf); |
1379 | 1380 | ||
1380 | /** | 1381 | static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) |
1381 | * vb2_qbuf() - Queue a buffer from userspace | ||
1382 | * @q: videobuf2 queue | ||
1383 | * @b: buffer structure passed from userspace to vidioc_qbuf handler | ||
1384 | * in driver | ||
1385 | * | ||
1386 | * Should be called from vidioc_qbuf ioctl handler of a driver. | ||
1387 | * This function: | ||
1388 | * 1) verifies the passed buffer, | ||
1389 | * 2) if necessary, calls buf_prepare callback in the driver (if provided), in | ||
1390 | * which driver-specific buffer initialization can be performed, | ||
1391 | * 3) if streaming is on, queues the buffer in driver by the means of buf_queue | ||
1392 | * callback for processing. | ||
1393 | * | ||
1394 | * The return values from this function are intended to be directly returned | ||
1395 | * from vidioc_qbuf handler in driver. | ||
1396 | */ | ||
1397 | int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) | ||
1398 | { | 1382 | { |
1399 | int ret = vb2_queue_or_prepare_buf(q, b, "qbuf"); | 1383 | int ret = vb2_queue_or_prepare_buf(q, b, "qbuf"); |
1400 | struct vb2_buffer *vb; | 1384 | struct vb2_buffer *vb; |
@@ -1445,6 +1429,33 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) | |||
1445 | dprintk(1, "%s() of buffer %d succeeded\n", __func__, vb->v4l2_buf.index); | 1429 | dprintk(1, "%s() of buffer %d succeeded\n", __func__, vb->v4l2_buf.index); |
1446 | return 0; | 1430 | return 0; |
1447 | } | 1431 | } |
1432 | |||
1433 | /** | ||
1434 | * vb2_qbuf() - Queue a buffer from userspace | ||
1435 | * @q: videobuf2 queue | ||
1436 | * @b: buffer structure passed from userspace to vidioc_qbuf handler | ||
1437 | * in driver | ||
1438 | * | ||
1439 | * Should be called from vidioc_qbuf ioctl handler of a driver. | ||
1440 | * This function: | ||
1441 | * 1) verifies the passed buffer, | ||
1442 | * 2) if necessary, calls buf_prepare callback in the driver (if provided), in | ||
1443 | * which driver-specific buffer initialization can be performed, | ||
1444 | * 3) if streaming is on, queues the buffer in driver by the means of buf_queue | ||
1445 | * callback for processing. | ||
1446 | * | ||
1447 | * The return values from this function are intended to be directly returned | ||
1448 | * from vidioc_qbuf handler in driver. | ||
1449 | */ | ||
1450 | int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) | ||
1451 | { | ||
1452 | if (q->fileio) { | ||
1453 | dprintk(1, "%s(): file io in progress\n", __func__); | ||
1454 | return -EBUSY; | ||
1455 | } | ||
1456 | |||
1457 | return vb2_internal_qbuf(q, b); | ||
1458 | } | ||
1448 | EXPORT_SYMBOL_GPL(vb2_qbuf); | 1459 | EXPORT_SYMBOL_GPL(vb2_qbuf); |
1449 | 1460 | ||
1450 | /** | 1461 | /** |
@@ -1593,37 +1604,11 @@ static void __vb2_dqbuf(struct vb2_buffer *vb) | |||
1593 | } | 1604 | } |
1594 | } | 1605 | } |
1595 | 1606 | ||
1596 | /** | 1607 | static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) |
1597 | * vb2_dqbuf() - Dequeue a buffer to the userspace | ||
1598 | * @q: videobuf2 queue | ||
1599 | * @b: buffer structure passed from userspace to vidioc_dqbuf handler | ||
1600 | * in driver | ||
1601 | * @nonblocking: if true, this call will not sleep waiting for a buffer if no | ||
1602 | * buffers ready for dequeuing are present. Normally the driver | ||
1603 | * would be passing (file->f_flags & O_NONBLOCK) here | ||
1604 | * | ||
1605 | * Should be called from vidioc_dqbuf ioctl handler of a driver. | ||
1606 | * This function: | ||
1607 | * 1) verifies the passed buffer, | ||
1608 | * 2) calls buf_finish callback in the driver (if provided), in which | ||
1609 | * driver can perform any additional operations that may be required before | ||
1610 | * returning the buffer to userspace, such as cache sync, | ||
1611 | * 3) the buffer struct members are filled with relevant information for | ||
1612 | * the userspace. | ||
1613 | * | ||
1614 | * The return values from this function are intended to be directly returned | ||
1615 | * from vidioc_dqbuf handler in driver. | ||
1616 | */ | ||
1617 | int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) | ||
1618 | { | 1608 | { |
1619 | struct vb2_buffer *vb = NULL; | 1609 | struct vb2_buffer *vb = NULL; |
1620 | int ret; | 1610 | int ret; |
1621 | 1611 | ||
1622 | if (q->fileio) { | ||
1623 | dprintk(1, "dqbuf: file io in progress\n"); | ||
1624 | return -EBUSY; | ||
1625 | } | ||
1626 | |||
1627 | if (b->type != q->type) { | 1612 | if (b->type != q->type) { |
1628 | dprintk(1, "dqbuf: invalid buffer type\n"); | 1613 | dprintk(1, "dqbuf: invalid buffer type\n"); |
1629 | return -EINVAL; | 1614 | return -EINVAL; |
@@ -1662,6 +1647,36 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) | |||
1662 | 1647 | ||
1663 | return 0; | 1648 | return 0; |
1664 | } | 1649 | } |
1650 | |||
1651 | /** | ||
1652 | * vb2_dqbuf() - Dequeue a buffer to the userspace | ||
1653 | * @q: videobuf2 queue | ||
1654 | * @b: buffer structure passed from userspace to vidioc_dqbuf handler | ||
1655 | * in driver | ||
1656 | * @nonblocking: if true, this call will not sleep waiting for a buffer if no | ||
1657 | * buffers ready for dequeuing are present. Normally the driver | ||
1658 | * would be passing (file->f_flags & O_NONBLOCK) here | ||
1659 | * | ||
1660 | * Should be called from vidioc_dqbuf ioctl handler of a driver. | ||
1661 | * This function: | ||
1662 | * 1) verifies the passed buffer, | ||
1663 | * 2) calls buf_finish callback in the driver (if provided), in which | ||
1664 | * driver can perform any additional operations that may be required before | ||
1665 | * returning the buffer to userspace, such as cache sync, | ||
1666 | * 3) the buffer struct members are filled with relevant information for | ||
1667 | * the userspace. | ||
1668 | * | ||
1669 | * The return values from this function are intended to be directly returned | ||
1670 | * from vidioc_dqbuf handler in driver. | ||
1671 | */ | ||
1672 | int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) | ||
1673 | { | ||
1674 | if (q->fileio) { | ||
1675 | dprintk(1, "dqbuf: file io in progress\n"); | ||
1676 | return -EBUSY; | ||
1677 | } | ||
1678 | return vb2_internal_dqbuf(q, b, nonblocking); | ||
1679 | } | ||
1665 | EXPORT_SYMBOL_GPL(vb2_dqbuf); | 1680 | EXPORT_SYMBOL_GPL(vb2_dqbuf); |
1666 | 1681 | ||
1667 | /** | 1682 | /** |
@@ -1701,29 +1716,11 @@ static void __vb2_queue_cancel(struct vb2_queue *q) | |||
1701 | __vb2_dqbuf(q->bufs[i]); | 1716 | __vb2_dqbuf(q->bufs[i]); |
1702 | } | 1717 | } |
1703 | 1718 | ||
1704 | /** | 1719 | static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type) |
1705 | * vb2_streamon - start streaming | ||
1706 | * @q: videobuf2 queue | ||
1707 | * @type: type argument passed from userspace to vidioc_streamon handler | ||
1708 | * | ||
1709 | * Should be called from vidioc_streamon handler of a driver. | ||
1710 | * This function: | ||
1711 | * 1) verifies current state | ||
1712 | * 2) passes any previously queued buffers to the driver and starts streaming | ||
1713 | * | ||
1714 | * The return values from this function are intended to be directly returned | ||
1715 | * from vidioc_streamon handler in the driver. | ||
1716 | */ | ||
1717 | int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) | ||
1718 | { | 1720 | { |
1719 | struct vb2_buffer *vb; | 1721 | struct vb2_buffer *vb; |
1720 | int ret; | 1722 | int ret; |
1721 | 1723 | ||
1722 | if (q->fileio) { | ||
1723 | dprintk(1, "streamon: file io in progress\n"); | ||
1724 | return -EBUSY; | ||
1725 | } | ||
1726 | |||
1727 | if (type != q->type) { | 1724 | if (type != q->type) { |
1728 | dprintk(1, "streamon: invalid stream type\n"); | 1725 | dprintk(1, "streamon: invalid stream type\n"); |
1729 | return -EINVAL; | 1726 | return -EINVAL; |
@@ -1756,31 +1753,32 @@ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) | |||
1756 | dprintk(3, "Streamon successful\n"); | 1753 | dprintk(3, "Streamon successful\n"); |
1757 | return 0; | 1754 | return 0; |
1758 | } | 1755 | } |
1759 | EXPORT_SYMBOL_GPL(vb2_streamon); | ||
1760 | |||
1761 | 1756 | ||
1762 | /** | 1757 | /** |
1763 | * vb2_streamoff - stop streaming | 1758 | * vb2_streamon - start streaming |
1764 | * @q: videobuf2 queue | 1759 | * @q: videobuf2 queue |
1765 | * @type: type argument passed from userspace to vidioc_streamoff handler | 1760 | * @type: type argument passed from userspace to vidioc_streamon handler |
1766 | * | 1761 | * |
1767 | * Should be called from vidioc_streamoff handler of a driver. | 1762 | * Should be called from vidioc_streamon handler of a driver. |
1768 | * This function: | 1763 | * This function: |
1769 | * 1) verifies current state, | 1764 | * 1) verifies current state |
1770 | * 2) stop streaming and dequeues any queued buffers, including those previously | 1765 | * 2) passes any previously queued buffers to the driver and starts streaming |
1771 | * passed to the driver (after waiting for the driver to finish). | ||
1772 | * | 1766 | * |
1773 | * This call can be used for pausing playback. | ||
1774 | * The return values from this function are intended to be directly returned | 1767 | * The return values from this function are intended to be directly returned |
1775 | * from vidioc_streamoff handler in the driver | 1768 | * from vidioc_streamon handler in the driver. |
1776 | */ | 1769 | */ |
1777 | int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) | 1770 | int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) |
1778 | { | 1771 | { |
1779 | if (q->fileio) { | 1772 | if (q->fileio) { |
1780 | dprintk(1, "streamoff: file io in progress\n"); | 1773 | dprintk(1, "streamon: file io in progress\n"); |
1781 | return -EBUSY; | 1774 | return -EBUSY; |
1782 | } | 1775 | } |
1776 | return vb2_internal_streamon(q, type); | ||
1777 | } | ||
1778 | EXPORT_SYMBOL_GPL(vb2_streamon); | ||
1783 | 1779 | ||
1780 | static int vb2_internal_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) | ||
1781 | { | ||
1784 | if (type != q->type) { | 1782 | if (type != q->type) { |
1785 | dprintk(1, "streamoff: invalid stream type\n"); | 1783 | dprintk(1, "streamoff: invalid stream type\n"); |
1786 | return -EINVAL; | 1784 | return -EINVAL; |
@@ -1800,6 +1798,30 @@ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) | |||
1800 | dprintk(3, "Streamoff successful\n"); | 1798 | dprintk(3, "Streamoff successful\n"); |
1801 | return 0; | 1799 | return 0; |
1802 | } | 1800 | } |
1801 | |||
1802 | /** | ||
1803 | * vb2_streamoff - stop streaming | ||
1804 | * @q: videobuf2 queue | ||
1805 | * @type: type argument passed from userspace to vidioc_streamoff handler | ||
1806 | * | ||
1807 | * Should be called from vidioc_streamoff handler of a driver. | ||
1808 | * This function: | ||
1809 | * 1) verifies current state, | ||
1810 | * 2) stop streaming and dequeues any queued buffers, including those previously | ||
1811 | * passed to the driver (after waiting for the driver to finish). | ||
1812 | * | ||
1813 | * This call can be used for pausing playback. | ||
1814 | * The return values from this function are intended to be directly returned | ||
1815 | * from vidioc_streamoff handler in the driver | ||
1816 | */ | ||
1817 | int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) | ||
1818 | { | ||
1819 | if (q->fileio) { | ||
1820 | dprintk(1, "streamoff: file io in progress\n"); | ||
1821 | return -EBUSY; | ||
1822 | } | ||
1823 | return vb2_internal_streamoff(q, type); | ||
1824 | } | ||
1803 | EXPORT_SYMBOL_GPL(vb2_streamoff); | 1825 | EXPORT_SYMBOL_GPL(vb2_streamoff); |
1804 | 1826 | ||
1805 | /** | 1827 | /** |
@@ -2322,13 +2344,8 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q) | |||
2322 | struct vb2_fileio_data *fileio = q->fileio; | 2344 | struct vb2_fileio_data *fileio = q->fileio; |
2323 | 2345 | ||
2324 | if (fileio) { | 2346 | if (fileio) { |
2325 | /* | 2347 | vb2_internal_streamoff(q, q->type); |
2326 | * Hack fileio context to enable direct calls to vb2 ioctl | ||
2327 | * interface. | ||
2328 | */ | ||
2329 | q->fileio = NULL; | 2348 | q->fileio = NULL; |
2330 | |||
2331 | vb2_streamoff(q, q->type); | ||
2332 | fileio->req.count = 0; | 2349 | fileio->req.count = 0; |
2333 | vb2_reqbufs(q, &fileio->req); | 2350 | vb2_reqbufs(q, &fileio->req); |
2334 | kfree(fileio); | 2351 | kfree(fileio); |
@@ -2371,12 +2388,6 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ | |||
2371 | } | 2388 | } |
2372 | fileio = q->fileio; | 2389 | fileio = q->fileio; |
2373 | 2390 | ||
2374 | /* | ||
2375 | * Hack fileio context to enable direct calls to vb2 ioctl interface. | ||
2376 | * The pointer will be restored before returning from this function. | ||
2377 | */ | ||
2378 | q->fileio = NULL; | ||
2379 | |||
2380 | index = fileio->index; | 2391 | index = fileio->index; |
2381 | buf = &fileio->bufs[index]; | 2392 | buf = &fileio->bufs[index]; |
2382 | 2393 | ||
@@ -2393,10 +2404,10 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ | |||
2393 | fileio->b.type = q->type; | 2404 | fileio->b.type = q->type; |
2394 | fileio->b.memory = q->memory; | 2405 | fileio->b.memory = q->memory; |
2395 | fileio->b.index = index; | 2406 | fileio->b.index = index; |
2396 | ret = vb2_dqbuf(q, &fileio->b, nonblock); | 2407 | ret = vb2_internal_dqbuf(q, &fileio->b, nonblock); |
2397 | dprintk(5, "file io: vb2_dqbuf result: %d\n", ret); | 2408 | dprintk(5, "file io: vb2_dqbuf result: %d\n", ret); |
2398 | if (ret) | 2409 | if (ret) |
2399 | goto end; | 2410 | return ret; |
2400 | fileio->dq_count += 1; | 2411 | fileio->dq_count += 1; |
2401 | 2412 | ||
2402 | /* | 2413 | /* |
@@ -2426,8 +2437,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ | |||
2426 | ret = copy_from_user(buf->vaddr + buf->pos, data, count); | 2437 | ret = copy_from_user(buf->vaddr + buf->pos, data, count); |
2427 | if (ret) { | 2438 | if (ret) { |
2428 | dprintk(3, "file io: error copying data\n"); | 2439 | dprintk(3, "file io: error copying data\n"); |
2429 | ret = -EFAULT; | 2440 | return -EFAULT; |
2430 | goto end; | ||
2431 | } | 2441 | } |
2432 | 2442 | ||
2433 | /* | 2443 | /* |
@@ -2447,10 +2457,6 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ | |||
2447 | if (read && (fileio->flags & VB2_FILEIO_READ_ONCE) && | 2457 | if (read && (fileio->flags & VB2_FILEIO_READ_ONCE) && |
2448 | fileio->dq_count == 1) { | 2458 | fileio->dq_count == 1) { |
2449 | dprintk(3, "file io: read limit reached\n"); | 2459 | dprintk(3, "file io: read limit reached\n"); |
2450 | /* | ||
2451 | * Restore fileio pointer and release the context. | ||
2452 | */ | ||
2453 | q->fileio = fileio; | ||
2454 | return __vb2_cleanup_fileio(q); | 2460 | return __vb2_cleanup_fileio(q); |
2455 | } | 2461 | } |
2456 | 2462 | ||
@@ -2462,10 +2468,10 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ | |||
2462 | fileio->b.memory = q->memory; | 2468 | fileio->b.memory = q->memory; |
2463 | fileio->b.index = index; | 2469 | fileio->b.index = index; |
2464 | fileio->b.bytesused = buf->pos; | 2470 | fileio->b.bytesused = buf->pos; |
2465 | ret = vb2_qbuf(q, &fileio->b); | 2471 | ret = vb2_internal_qbuf(q, &fileio->b); |
2466 | dprintk(5, "file io: vb2_dbuf result: %d\n", ret); | 2472 | dprintk(5, "file io: vb2_dbuf result: %d\n", ret); |
2467 | if (ret) | 2473 | if (ret) |
2468 | goto end; | 2474 | return ret; |
2469 | 2475 | ||
2470 | /* | 2476 | /* |
2471 | * Buffer has been queued, update the status | 2477 | * Buffer has been queued, update the status |
@@ -2484,9 +2490,9 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ | |||
2484 | * Start streaming if required. | 2490 | * Start streaming if required. |
2485 | */ | 2491 | */ |
2486 | if (!read && !q->streaming) { | 2492 | if (!read && !q->streaming) { |
2487 | ret = vb2_streamon(q, q->type); | 2493 | ret = vb2_internal_streamon(q, q->type); |
2488 | if (ret) | 2494 | if (ret) |
2489 | goto end; | 2495 | return ret; |
2490 | } | 2496 | } |
2491 | } | 2497 | } |
2492 | 2498 | ||
@@ -2495,11 +2501,6 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ | |||
2495 | */ | 2501 | */ |
2496 | if (ret == 0) | 2502 | if (ret == 0) |
2497 | ret = count; | 2503 | ret = count; |
2498 | end: | ||
2499 | /* | ||
2500 | * Restore the fileio context and block vb2 ioctl interface. | ||
2501 | */ | ||
2502 | q->fileio = fileio; | ||
2503 | return ret; | 2504 | return ret; |
2504 | } | 2505 | } |
2505 | 2506 | ||