diff options
author | Jarod Wilson <jarod@redhat.com> | 2010-06-16 16:10:05 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-08-02 13:55:47 -0400 |
commit | 657290b63efabfbb2862a6089c3fd5dbcc8e9037 (patch) | |
tree | bc6d92bfe4a8ec4b15d933fa97c9079b1fc15567 /drivers/media/IR | |
parent | bd3881b1cedbe6733097bd87da069bd174cc7052 (diff) |
V4L/DVB: IR/mceusb: misc cleanups and init fixes
The first-gen mceusb device init code, while mostly functional, had a few
issues in it. This patch does the following:
1) removes use of magic numbers
2) eliminates mapping of memory from stack
3) makes debug spew translator functional
Additionally, this clean-up revealed that we cannot read the proper default
tx blaster bitmask from the device, we do actually have to initialize it
ourselves, which requires use of a somewhat gross list-based mask inversion
check.
This patch also removes the entirely unnecessary use of struct ir_input_state.
Also supersedes two earlier patches that also touched on first-gen
cleanup, but were partially botched. This one actually compiles, works,
etc., I swear. ;)
Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/IR')
-rw-r--r-- | drivers/media/IR/mceusb.c | 138 |
1 files changed, 69 insertions, 69 deletions
diff --git a/drivers/media/IR/mceusb.c b/drivers/media/IR/mceusb.c index c9dd2f843855..756f7186edb8 100644 --- a/drivers/media/IR/mceusb.c +++ b/drivers/media/IR/mceusb.c | |||
@@ -52,6 +52,8 @@ | |||
52 | 52 | ||
53 | #define USB_BUFLEN 32 /* USB reception buffer length */ | 53 | #define USB_BUFLEN 32 /* USB reception buffer length */ |
54 | #define IRBUF_SIZE 256 /* IR work buffer length */ | 54 | #define IRBUF_SIZE 256 /* IR work buffer length */ |
55 | #define USB_CTRL_MSG_SZ 2 /* Size of usb ctrl msg on gen1 hw */ | ||
56 | #define MCE_G1_INIT_MSGS 40 /* Init messages on gen1 hw to throw out */ | ||
55 | 57 | ||
56 | /* MCE constants */ | 58 | /* MCE constants */ |
57 | #define MCE_CMDBUF_SIZE 384 /* MCE Command buffer length */ | 59 | #define MCE_CMDBUF_SIZE 384 /* MCE Command buffer length */ |
@@ -217,12 +219,27 @@ static struct usb_device_id microsoft_gen1_list[] = { | |||
217 | {} | 219 | {} |
218 | }; | 220 | }; |
219 | 221 | ||
222 | static struct usb_device_id std_tx_mask_list[] = { | ||
223 | { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) }, | ||
224 | { USB_DEVICE(VENDOR_PHILIPS, 0x060c) }, | ||
225 | { USB_DEVICE(VENDOR_SMK, 0x031d) }, | ||
226 | { USB_DEVICE(VENDOR_SMK, 0x0322) }, | ||
227 | { USB_DEVICE(VENDOR_SMK, 0x0334) }, | ||
228 | { USB_DEVICE(VENDOR_TOPSEED, 0x0001) }, | ||
229 | { USB_DEVICE(VENDOR_TOPSEED, 0x0006) }, | ||
230 | { USB_DEVICE(VENDOR_TOPSEED, 0x0007) }, | ||
231 | { USB_DEVICE(VENDOR_TOPSEED, 0x0008) }, | ||
232 | { USB_DEVICE(VENDOR_TOPSEED, 0x000a) }, | ||
233 | { USB_DEVICE(VENDOR_TOPSEED, 0x0011) }, | ||
234 | { USB_DEVICE(VENDOR_PINNACLE, 0x0225) }, | ||
235 | {} | ||
236 | }; | ||
237 | |||
220 | /* data structure for each usb transceiver */ | 238 | /* data structure for each usb transceiver */ |
221 | struct mceusb_dev { | 239 | struct mceusb_dev { |
222 | /* ir-core bits */ | 240 | /* ir-core bits */ |
223 | struct ir_input_dev *irdev; | 241 | struct ir_input_dev *irdev; |
224 | struct ir_dev_props *props; | 242 | struct ir_dev_props *props; |
225 | struct ir_input_state *state; | ||
226 | struct ir_raw_event rawir; | 243 | struct ir_raw_event rawir; |
227 | 244 | ||
228 | /* core device bits */ | 245 | /* core device bits */ |
@@ -245,7 +262,7 @@ struct mceusb_dev { | |||
245 | 262 | ||
246 | struct { | 263 | struct { |
247 | u32 connected:1; | 264 | u32 connected:1; |
248 | u32 def_xmit_mask_set:1; | 265 | u32 tx_mask_inverted:1; |
249 | u32 microsoft_gen1:1; | 266 | u32 microsoft_gen1:1; |
250 | u32 gen3:1; | 267 | u32 gen3:1; |
251 | u32 reserved:28; | 268 | u32 reserved:28; |
@@ -258,8 +275,7 @@ struct mceusb_dev { | |||
258 | char name[128]; | 275 | char name[128]; |
259 | char phys[64]; | 276 | char phys[64]; |
260 | 277 | ||
261 | unsigned char def_xmit_mask; | 278 | unsigned char tx_mask; |
262 | unsigned char cur_xmit_mask; | ||
263 | }; | 279 | }; |
264 | 280 | ||
265 | /* | 281 | /* |
@@ -307,11 +323,13 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, | |||
307 | int i; | 323 | int i; |
308 | u8 cmd, subcmd, data1, data2; | 324 | u8 cmd, subcmd, data1, data2; |
309 | struct device *dev = ir->dev; | 325 | struct device *dev = ir->dev; |
326 | int idx = 0; | ||
310 | 327 | ||
311 | if (len <= 0) | 328 | /* skip meaningless 0xb1 0x60 header bytes on orig receiver */ |
312 | return; | 329 | if (ir->flags.microsoft_gen1 && !out) |
330 | idx = 2; | ||
313 | 331 | ||
314 | if (ir->flags.microsoft_gen1 && len <= 2) | 332 | if (len <= idx) |
315 | return; | 333 | return; |
316 | 334 | ||
317 | for (i = 0; i < len && i < USB_BUFLEN; i++) | 335 | for (i = 0; i < len && i < USB_BUFLEN; i++) |
@@ -325,10 +343,10 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, | |||
325 | else | 343 | else |
326 | strcpy(inout, "Got\0"); | 344 | strcpy(inout, "Got\0"); |
327 | 345 | ||
328 | cmd = buf[0] & 0xff; | 346 | cmd = buf[idx] & 0xff; |
329 | subcmd = buf[1] & 0xff; | 347 | subcmd = buf[idx + 1] & 0xff; |
330 | data1 = buf[2] & 0xff; | 348 | data1 = buf[idx + 2] & 0xff; |
331 | data2 = buf[3] & 0xff; | 349 | data2 = buf[idx + 3] & 0xff; |
332 | 350 | ||
333 | switch (cmd) { | 351 | switch (cmd) { |
334 | case 0x00: | 352 | case 0x00: |
@@ -346,7 +364,7 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, | |||
346 | else | 364 | else |
347 | dev_info(dev, "hw/sw rev 0x%02x 0x%02x " | 365 | dev_info(dev, "hw/sw rev 0x%02x 0x%02x " |
348 | "0x%02x 0x%02x\n", data1, data2, | 366 | "0x%02x 0x%02x\n", data1, data2, |
349 | buf[4], buf[5]); | 367 | buf[idx + 4], buf[idx + 5]); |
350 | break; | 368 | break; |
351 | case 0xaa: | 369 | case 0xaa: |
352 | dev_info(dev, "Device reset requested\n"); | 370 | dev_info(dev, "Device reset requested\n"); |
@@ -507,6 +525,19 @@ static void mce_sync_in(struct mceusb_dev *ir, unsigned char *data, int size) | |||
507 | mce_request_packet(ir, ir->usb_ep_in, data, size, MCEUSB_RX); | 525 | mce_request_packet(ir, ir->usb_ep_in, data, size, MCEUSB_RX); |
508 | } | 526 | } |
509 | 527 | ||
528 | /* Sets active IR outputs -- mce devices typically (all?) have two */ | ||
529 | static int mceusb_set_tx_mask(void *priv, u32 mask) | ||
530 | { | ||
531 | struct mceusb_dev *ir = priv; | ||
532 | |||
533 | if (ir->flags.tx_mask_inverted) | ||
534 | ir->tx_mask = (mask != 0x03 ? mask ^ 0x03 : mask) << 1; | ||
535 | else | ||
536 | ir->tx_mask = mask; | ||
537 | |||
538 | return 0; | ||
539 | } | ||
540 | |||
510 | static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) | 541 | static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) |
511 | { | 542 | { |
512 | struct ir_raw_event rawir = { .pulse = false, .duration = 0 }; | 543 | struct ir_raw_event rawir = { .pulse = false, .duration = 0 }; |
@@ -568,24 +599,6 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) | |||
568 | } | 599 | } |
569 | } | 600 | } |
570 | 601 | ||
571 | static void mceusb_set_default_xmit_mask(struct urb *urb) | ||
572 | { | ||
573 | struct mceusb_dev *ir = urb->context; | ||
574 | char *buffer = urb->transfer_buffer; | ||
575 | u8 cmd, subcmd, def_xmit_mask; | ||
576 | |||
577 | cmd = buffer[0] & 0xff; | ||
578 | subcmd = buffer[1] & 0xff; | ||
579 | |||
580 | if (cmd == 0x9f && subcmd == 0x08) { | ||
581 | def_xmit_mask = buffer[2] & 0xff; | ||
582 | dev_dbg(ir->dev, "%s: setting xmit mask to 0x%02x\n", | ||
583 | __func__, def_xmit_mask); | ||
584 | ir->def_xmit_mask = def_xmit_mask; | ||
585 | ir->flags.def_xmit_mask_set = 1; | ||
586 | } | ||
587 | } | ||
588 | |||
589 | static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs) | 602 | static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs) |
590 | { | 603 | { |
591 | struct mceusb_dev *ir; | 604 | struct mceusb_dev *ir; |
@@ -602,9 +615,6 @@ static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs) | |||
602 | 615 | ||
603 | buf_len = urb->actual_length; | 616 | buf_len = urb->actual_length; |
604 | 617 | ||
605 | if (!ir->flags.def_xmit_mask_set) | ||
606 | mceusb_set_default_xmit_mask(urb); | ||
607 | |||
608 | if (debug) | 618 | if (debug) |
609 | mceusb_dev_printdata(ir, urb->transfer_buffer, buf_len, false); | 619 | mceusb_dev_printdata(ir, urb->transfer_buffer, buf_len, false); |
610 | 620 | ||
@@ -637,27 +647,38 @@ static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs) | |||
637 | static void mceusb_gen1_init(struct mceusb_dev *ir) | 647 | static void mceusb_gen1_init(struct mceusb_dev *ir) |
638 | { | 648 | { |
639 | int i, ret; | 649 | int i, ret; |
640 | char junk[64], data[8]; | ||
641 | int partial = 0; | 650 | int partial = 0; |
642 | struct device *dev = ir->dev; | 651 | struct device *dev = ir->dev; |
652 | char *junk, *data; | ||
653 | |||
654 | junk = kmalloc(2 * USB_BUFLEN, GFP_KERNEL); | ||
655 | if (!junk) { | ||
656 | dev_err(dev, "%s: memory allocation failed!\n", __func__); | ||
657 | return; | ||
658 | } | ||
659 | |||
660 | data = kzalloc(USB_CTRL_MSG_SZ, GFP_KERNEL); | ||
661 | if (!data) { | ||
662 | dev_err(dev, "%s: memory allocation failed!\n", __func__); | ||
663 | kfree(junk); | ||
664 | return; | ||
665 | } | ||
643 | 666 | ||
644 | /* | 667 | /* |
645 | * Clear off the first few messages. These look like calibration | 668 | * Clear off the first few messages. These look like calibration |
646 | * or test data, I can't really tell. This also flushes in case | 669 | * or test data, I can't really tell. This also flushes in case |
647 | * we have random ir data queued up. | 670 | * we have random ir data queued up. |
648 | */ | 671 | */ |
649 | for (i = 0; i < 40; i++) | 672 | for (i = 0; i < MCE_G1_INIT_MSGS; i++) |
650 | usb_bulk_msg(ir->usbdev, | 673 | usb_bulk_msg(ir->usbdev, |
651 | usb_rcvbulkpipe(ir->usbdev, | 674 | usb_rcvbulkpipe(ir->usbdev, |
652 | ir->usb_ep_in->bEndpointAddress), | 675 | ir->usb_ep_in->bEndpointAddress), |
653 | junk, 64, &partial, HZ * 10); | 676 | junk, sizeof(junk), &partial, HZ * 10); |
654 | |||
655 | memset(data, 0, 8); | ||
656 | 677 | ||
657 | /* Get Status */ | 678 | /* Get Status */ |
658 | ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), | 679 | ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), |
659 | USB_REQ_GET_STATUS, USB_DIR_IN, | 680 | USB_REQ_GET_STATUS, USB_DIR_IN, |
660 | 0, 0, data, 2, HZ * 3); | 681 | 0, 0, data, USB_CTRL_MSG_SZ, HZ * 3); |
661 | 682 | ||
662 | /* ret = usb_get_status( ir->usbdev, 0, 0, data ); */ | 683 | /* ret = usb_get_status( ir->usbdev, 0, 0, data ); */ |
663 | dev_dbg(dev, "%s - ret = %d status = 0x%x 0x%x\n", __func__, | 684 | dev_dbg(dev, "%s - ret = %d status = 0x%x 0x%x\n", __func__, |
@@ -667,11 +688,11 @@ static void mceusb_gen1_init(struct mceusb_dev *ir) | |||
667 | * This is a strange one. They issue a set address to the device | 688 | * This is a strange one. They issue a set address to the device |
668 | * on the receive control pipe and expect a certain value pair back | 689 | * on the receive control pipe and expect a certain value pair back |
669 | */ | 690 | */ |
670 | memset(data, 0, 8); | 691 | memset(data, 0, sizeof(data)); |
671 | 692 | ||
672 | ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), | 693 | ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), |
673 | USB_REQ_SET_ADDRESS, USB_TYPE_VENDOR, 0, 0, | 694 | USB_REQ_SET_ADDRESS, USB_TYPE_VENDOR, 0, 0, |
674 | data, 2, HZ * 3); | 695 | data, USB_CTRL_MSG_SZ, HZ * 3); |
675 | dev_dbg(dev, "%s - ret = %d\n", __func__, ret); | 696 | dev_dbg(dev, "%s - ret = %d\n", __func__, ret); |
676 | dev_dbg(dev, "%s - data[0] = %d, data[1] = %d\n", | 697 | dev_dbg(dev, "%s - data[0] = %d, data[1] = %d\n", |
677 | __func__, data[0], data[1]); | 698 | __func__, data[0], data[1]); |
@@ -694,6 +715,9 @@ static void mceusb_gen1_init(struct mceusb_dev *ir) | |||
694 | 2, USB_TYPE_VENDOR, | 715 | 2, USB_TYPE_VENDOR, |
695 | 0x0000, 0x0100, NULL, 0, HZ * 3); | 716 | 0x0000, 0x0100, NULL, 0, HZ * 3); |
696 | dev_dbg(dev, "%s - retC = %d\n", __func__, ret); | 717 | dev_dbg(dev, "%s - retC = %d\n", __func__, ret); |
718 | |||
719 | kfree(data); | ||
720 | kfree(junk); | ||
697 | }; | 721 | }; |
698 | 722 | ||
699 | static void mceusb_gen2_init(struct mceusb_dev *ir) | 723 | static void mceusb_gen2_init(struct mceusb_dev *ir) |
@@ -748,7 +772,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir) | |||
748 | struct input_dev *idev; | 772 | struct input_dev *idev; |
749 | struct ir_dev_props *props; | 773 | struct ir_dev_props *props; |
750 | struct ir_input_dev *irdev; | 774 | struct ir_input_dev *irdev; |
751 | struct ir_input_state *state; | ||
752 | struct device *dev = ir->dev; | 775 | struct device *dev = ir->dev; |
753 | int ret = -ENODEV; | 776 | int ret = -ENODEV; |
754 | 777 | ||
@@ -771,42 +794,22 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir) | |||
771 | goto ir_dev_alloc_failed; | 794 | goto ir_dev_alloc_failed; |
772 | } | 795 | } |
773 | 796 | ||
774 | state = kzalloc(sizeof(struct ir_input_state), GFP_KERNEL); | ||
775 | if (!state) { | ||
776 | dev_err(dev, "remote ir state allocation failed\n"); | ||
777 | goto ir_state_alloc_failed; | ||
778 | } | ||
779 | |||
780 | snprintf(ir->name, sizeof(ir->name), "Media Center Edition eHome " | 797 | snprintf(ir->name, sizeof(ir->name), "Media Center Edition eHome " |
781 | "Infrared Remote Transceiver (%04x:%04x)", | 798 | "Infrared Remote Transceiver (%04x:%04x)", |
782 | le16_to_cpu(ir->usbdev->descriptor.idVendor), | 799 | le16_to_cpu(ir->usbdev->descriptor.idVendor), |
783 | le16_to_cpu(ir->usbdev->descriptor.idProduct)); | 800 | le16_to_cpu(ir->usbdev->descriptor.idProduct)); |
784 | 801 | ||
785 | ret = ir_input_init(idev, state, IR_TYPE_RC6); | ||
786 | if (ret < 0) | ||
787 | goto irdev_failed; | ||
788 | |||
789 | idev->name = ir->name; | 802 | idev->name = ir->name; |
790 | |||
791 | usb_make_path(ir->usbdev, ir->phys, sizeof(ir->phys)); | 803 | usb_make_path(ir->usbdev, ir->phys, sizeof(ir->phys)); |
792 | strlcat(ir->phys, "/input0", sizeof(ir->phys)); | 804 | strlcat(ir->phys, "/input0", sizeof(ir->phys)); |
793 | idev->phys = ir->phys; | 805 | idev->phys = ir->phys; |
794 | 806 | ||
795 | /* FIXME: no EV_REP (yet), we may need our own auto-repeat handling */ | ||
796 | idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); | ||
797 | |||
798 | idev->keybit[BIT_WORD(BTN_MOUSE)] = | ||
799 | BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); | ||
800 | idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) | | ||
801 | BIT_MASK(REL_WHEEL); | ||
802 | |||
803 | props->priv = ir; | 807 | props->priv = ir; |
804 | props->driver_type = RC_DRIVER_IR_RAW; | 808 | props->driver_type = RC_DRIVER_IR_RAW; |
805 | props->allowed_protos = IR_TYPE_ALL; | 809 | props->allowed_protos = IR_TYPE_ALL; |
806 | 810 | ||
807 | ir->props = props; | 811 | ir->props = props; |
808 | ir->irdev = irdev; | 812 | ir->irdev = irdev; |
809 | ir->state = state; | ||
810 | 813 | ||
811 | input_set_drvdata(idev, irdev); | 814 | input_set_drvdata(idev, irdev); |
812 | 815 | ||
@@ -819,8 +822,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir) | |||
819 | return idev; | 822 | return idev; |
820 | 823 | ||
821 | irdev_failed: | 824 | irdev_failed: |
822 | kfree(state); | ||
823 | ir_state_alloc_failed: | ||
824 | kfree(irdev); | 825 | kfree(irdev); |
825 | ir_dev_alloc_failed: | 826 | ir_dev_alloc_failed: |
826 | kfree(props); | 827 | kfree(props); |
@@ -846,6 +847,7 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf, | |||
846 | bool is_gen3; | 847 | bool is_gen3; |
847 | bool is_microsoft_gen1; | 848 | bool is_microsoft_gen1; |
848 | bool is_pinnacle; | 849 | bool is_pinnacle; |
850 | bool tx_mask_inverted; | ||
849 | 851 | ||
850 | dev_dbg(&intf->dev, ": %s called\n", __func__); | 852 | dev_dbg(&intf->dev, ": %s called\n", __func__); |
851 | 853 | ||
@@ -857,6 +859,7 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf, | |||
857 | is_gen3 = usb_match_id(intf, gen3_list) ? 1 : 0; | 859 | is_gen3 = usb_match_id(intf, gen3_list) ? 1 : 0; |
858 | is_microsoft_gen1 = usb_match_id(intf, microsoft_gen1_list) ? 1 : 0; | 860 | is_microsoft_gen1 = usb_match_id(intf, microsoft_gen1_list) ? 1 : 0; |
859 | is_pinnacle = usb_match_id(intf, pinnacle_list) ? 1 : 0; | 861 | is_pinnacle = usb_match_id(intf, pinnacle_list) ? 1 : 0; |
862 | tx_mask_inverted = usb_match_id(intf, std_tx_mask_list) ? 0 : 1; | ||
860 | 863 | ||
861 | /* step through the endpoints to find first bulk in and out endpoint */ | 864 | /* step through the endpoints to find first bulk in and out endpoint */ |
862 | for (i = 0; i < idesc->desc.bNumEndpoints; ++i) { | 865 | for (i = 0; i < idesc->desc.bNumEndpoints; ++i) { |
@@ -933,6 +936,7 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf, | |||
933 | ir->len_in = maxp; | 936 | ir->len_in = maxp; |
934 | ir->flags.gen3 = is_gen3; | 937 | ir->flags.gen3 = is_gen3; |
935 | ir->flags.microsoft_gen1 = is_microsoft_gen1; | 938 | ir->flags.microsoft_gen1 = is_microsoft_gen1; |
939 | ir->flags.tx_mask_inverted = tx_mask_inverted; | ||
936 | 940 | ||
937 | /* Saving usb interface data for use by the transmitter routine */ | 941 | /* Saving usb interface data for use by the transmitter routine */ |
938 | ir->usb_ep_in = ep_in; | 942 | ir->usb_ep_in = ep_in; |
@@ -983,11 +987,7 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf, | |||
983 | 987 | ||
984 | mce_sync_in(ir, NULL, maxp); | 988 | mce_sync_in(ir, NULL, maxp); |
985 | 989 | ||
986 | /* We've already done this on gen3 devices */ | 990 | mceusb_set_tx_mask(ir, MCE_DEFAULT_TX_MASK); |
987 | if (!ir->flags.def_xmit_mask_set) { | ||
988 | mce_async_out(ir, GET_TX_BITMASK, sizeof(GET_TX_BITMASK)); | ||
989 | mce_sync_in(ir, NULL, maxp); | ||
990 | } | ||
991 | 991 | ||
992 | usb_set_intfdata(intf, ir); | 992 | usb_set_intfdata(intf, ir); |
993 | 993 | ||