From 04646f6a5ffec4a5cd55999e85df839278860cb1 Mon Sep 17 00:00:00 2001 From: Aron Szabo Date: Sat, 16 Jun 2012 12:15:55 +0200 Subject: [PATCH 016/222] lirc: added support for RaspberryPi GPIO lirc_rpi: Use read_current_timer to determine transmitter delay. Thanks to jjmz and others See: https://github.com/raspberrypi/linux/issues/525 lirc: Remove restriction on gpio pins that can be used with lirc Compute Module, for example could use different pins lirc_rpi: Add parameter to specify input pin pull Depending on the connected IR circuitry it might be desirable to change the gpios internal pull from it pull-down default behaviour. Add a module parameter to allow the user to set it explicitly. Signed-off-by: Julian Scheel lirc-rpi: Use the higher-level irq control functions This module used to access the irq_chip methods of the gpio controller directly, rather than going through the standard enable_irq/irq_set_irq_type functions. This caused problems on pinctrl-bcm2835 which only implements the irq_enable/disable methods and not irq_unmask/mask. lirc-rpi: Correct the interrupt usage 1) Correct the use of enable_irq (i.e. don't call it so often) 2) Correct the shutdown sequence. 3) Avoid a bcm2708_gpio driver quirk by setting the irq flags earlier lirc-rpi: use getnstimeofday instead of read_current_timer read_current_timer isn't guaranteed to return values in microseconds, and indeed it doesn't on a Pi2. Issue: linux#827 lirc-rpi: Add device tree support, and a suitable overlay The overlay supports DT parameters that match the old module parameters, except that gpio_in_pull should be set using the strings "up", "down" or "off". lirc-rpi: Also support pinctrl-bcm2835 in non-DT mode --- drivers/staging/media/lirc/Kconfig | 6 + drivers/staging/media/lirc/Makefile | 1 + drivers/staging/media/lirc/lirc_rpi.c | 765 ++++++++++++++++++++++++++++++++++ 3 files changed, 772 insertions(+) create mode 100644 drivers/staging/media/lirc/lirc_rpi.c --- a/drivers/staging/media/lirc/Kconfig +++ b/drivers/staging/media/lirc/Kconfig @@ -32,6 +32,12 @@ config LIRC_PARALLEL help Driver for Homebrew Parallel Port Receivers +config LIRC_RPI + tristate "Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi" + depends on LIRC + help + Driver for Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi + config LIRC_SASEM tristate "Sasem USB IR Remote" depends on LIRC && USB --- a/drivers/staging/media/lirc/Makefile +++ b/drivers/staging/media/lirc/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o obj-$(CONFIG_LIRC_IMON) += lirc_imon.o obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o +obj-$(CONFIG_LIRC_RPI) += lirc_rpi.o obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o obj-$(CONFIG_LIRC_SIR) += lirc_sir.o --- /dev/null +++ b/drivers/staging/media/lirc/lirc_rpi.c @@ -0,0 +1,765 @@ +/* + * lirc_rpi.c + * + * lirc_rpi - Device driver that records pulse- and pause-lengths + * (space-lengths) (just like the lirc_serial driver does) + * between GPIO interrupt events on the Raspberry Pi. + * Lots of code has been taken from the lirc_serial module, + * so I would like say thanks to the authors. + * + * Copyright (C) 2012 Aron Robert Szabo , + * Michael Bishop + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define LIRC_DRIVER_NAME "lirc_rpi" +#define RBUF_LEN 256 +#define LIRC_TRANSMITTER_LATENCY 50 + +#ifndef MAX_UDELAY_MS +#define MAX_UDELAY_US 5000 +#else +#define MAX_UDELAY_US (MAX_UDELAY_MS*1000) +#endif + +#define dprintk(fmt, args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ + fmt, ## args); \ + } while (0) + +/* module parameters */ + +/* set the default GPIO input pin */ +static int gpio_in_pin = 18; +/* set the default pull behaviour for input pin */ +static int gpio_in_pull = BCM2708_PULL_DOWN; +/* set the default GPIO output pin */ +static int gpio_out_pin = 17; +/* enable debugging messages */ +static bool debug; +/* -1 = auto, 0 = active high, 1 = active low */ +static int sense = -1; +/* use softcarrier by default */ +static bool softcarrier = 1; +/* 0 = do not invert output, 1 = invert output */ +static bool invert = 0; + +struct gpio_chip *gpiochip; +static int irq_num; + +/* forward declarations */ +static long send_pulse(unsigned long length); +static void send_space(long length); +static void lirc_rpi_exit(void); + +static struct platform_device *lirc_rpi_dev; +static struct timeval lasttv = { 0, 0 }; +static struct lirc_buffer rbuf; +static spinlock_t lock; + +/* initialized/set in init_timing_params() */ +static unsigned int freq = 38000; +static unsigned int duty_cycle = 50; +static unsigned long period; +static unsigned long pulse_width; +static unsigned long space_width; + +static void safe_udelay(unsigned long usecs) +{ + while (usecs > MAX_UDELAY_US) { + udelay(MAX_UDELAY_US); + usecs -= MAX_UDELAY_US; + } + udelay(usecs); +} + +static unsigned long read_current_us(void) +{ + struct timespec now; + getnstimeofday(&now); + return (now.tv_sec * 1000000) + (now.tv_nsec/1000); +} + +static int init_timing_params(unsigned int new_duty_cycle, + unsigned int new_freq) +{ + if (1000 * 1000000L / new_freq * new_duty_cycle / 100 <= + LIRC_TRANSMITTER_LATENCY) + return -EINVAL; + if (1000 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <= + LIRC_TRANSMITTER_LATENCY) + return -EINVAL; + duty_cycle = new_duty_cycle; + freq = new_freq; + period = 1000 * 1000000L / freq; + pulse_width = period * duty_cycle / 100; + space_width = period - pulse_width; + dprintk("in init_timing_params, freq=%d pulse=%ld, " + "space=%ld\n", freq, pulse_width, space_width); + return 0; +} + +static long send_pulse_softcarrier(unsigned long length) +{ + int flag; + unsigned long actual, target; + unsigned long actual_us, initial_us, target_us; + + length *= 1000; + + actual = 0; target = 0; flag = 0; + actual_us = read_current_us(); + + while (actual < length) { + if (flag) { + gpiochip->set(gpiochip, gpio_out_pin, invert); + target += space_width; + } else { + gpiochip->set(gpiochip, gpio_out_pin, !invert); + target += pulse_width; + } + initial_us = actual_us; + target_us = actual_us + (target - actual) / 1000; + /* + * Note - we've checked in ioctl that the pulse/space + * widths are big enough so that d is > 0 + */ + if ((int)(target_us - actual_us) > 0) + udelay(target_us - actual_us); + actual_us = read_current_us(); + actual += (actual_us - initial_us) * 1000; + flag = !flag; + } + return (actual-length) / 1000; +} + +static long send_pulse(unsigned long length) +{ + if (length <= 0) + return 0; + + if (softcarrier) { + return send_pulse_softcarrier(length); + } else { + gpiochip->set(gpiochip, gpio_out_pin, !invert); + safe_udelay(length); + return 0; + } +} + +static void send_space(long length) +{ + gpiochip->set(gpiochip, gpio_out_pin, invert); + if (length <= 0) + return; + safe_udelay(length); +} + +static void rbwrite(int l) +{ + if (lirc_buffer_full(&rbuf)) { + /* no new signals will be accepted */ + dprintk("Buffer overrun\n"); + return; + } + lirc_buffer_write(&rbuf, (void *)&l); +} + +static void frbwrite(int l) +{ + /* simple noise filter */ + static int pulse, space; + static unsigned int ptr; + + if (ptr > 0 && (l & PULSE_BIT)) { + pulse += l & PULSE_MASK; + if (pulse > 250) { + rbwrite(space); + rbwrite(pulse | PULSE_BIT); + ptr = 0; + pulse = 0; + } + return; + } + if (!(l & PULSE_BIT)) { + if (ptr == 0) { + if (l > 20000) { + space = l; + ptr++; + return; + } + } else { + if (l > 20000) { + space += pulse; + if (space > PULSE_MASK) + space = PULSE_MASK; + space += l; + if (space > PULSE_MASK) + space = PULSE_MASK; + pulse = 0; + return; + } + rbwrite(space); + rbwrite(pulse | PULSE_BIT); + ptr = 0; + pulse = 0; + } + } + rbwrite(l); +} + +static irqreturn_t irq_handler(int i, void *blah, struct pt_regs *regs) +{ + struct timeval tv; + long deltv; + int data; + int signal; + + /* use the GPIO signal level */ + signal = gpiochip->get(gpiochip, gpio_in_pin); + + if (sense != -1) { + /* get current time */ + do_gettimeofday(&tv); + + /* calc time since last interrupt in microseconds */ + deltv = tv.tv_sec-lasttv.tv_sec; + if (tv.tv_sec < lasttv.tv_sec || + (tv.tv_sec == lasttv.tv_sec && + tv.tv_usec < lasttv.tv_usec)) { + printk(KERN_WARNING LIRC_DRIVER_NAME + ": AIEEEE: your clock just jumped backwards\n"); + printk(KERN_WARNING LIRC_DRIVER_NAME + ": %d %d %lx %lx %lx %lx\n", signal, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); + data = PULSE_MASK; + } else if (deltv > 15) { + data = PULSE_MASK; /* really long time */ + if (!(signal^sense)) { + /* sanity check */ + printk(KERN_WARNING LIRC_DRIVER_NAME + ": AIEEEE: %d %d %lx %lx %lx %lx\n", + signal, sense, tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); + /* + * detecting pulse while this + * MUST be a space! + */ + sense = sense ? 0 : 1; + } + } else { + data = (int) (deltv*1000000 + + (tv.tv_usec - lasttv.tv_usec)); + } + frbwrite(signal^sense ? data : (data|PULSE_BIT)); + lasttv = tv; + wake_up_interruptible(&rbuf.wait_poll); + } + + return IRQ_HANDLED; +} + +static int is_right_chip(struct gpio_chip *chip, void *data) +{ + dprintk("is_right_chip %s %d\n", chip->label, strcmp(data, chip->label)); + + if (strcmp(data, chip->label) == 0) + return 1; + return 0; +} + +static inline int read_bool_property(const struct device_node *np, + const char *propname, + bool *out_value) +{ + u32 value = 0; + int err = of_property_read_u32(np, propname, &value); + if (err == 0) + *out_value = (value != 0); + return err; +} + +static void read_pin_settings(struct device_node *node) +{ + u32 pin; + int index; + + for (index = 0; + of_property_read_u32_index( + node, + "brcm,pins", + index, + &pin) == 0; + index++) { + u32 function; + int err; + err = of_property_read_u32_index( + node, + "brcm,function", + index, + &function); + if (err == 0) { + if (function == 1) /* Output */ + gpio_out_pin = pin; + else if (function == 0) /* Input */ + gpio_in_pin = pin; + } + } +} + +static int init_port(void) +{ + int i, nlow, nhigh, ret; + struct device_node *node; + + node = lirc_rpi_dev->dev.of_node; + + gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip); + + /* + * Because of the lack of a setpull function, only support + * pinctrl-bcm2835 if using device tree. + */ + if (!gpiochip && node) + gpiochip = gpiochip_find("pinctrl-bcm2835", is_right_chip); + + if (!gpiochip) { + pr_err(LIRC_DRIVER_NAME ": gpio chip not found!\n"); + return -ENODEV; + } + + if (node) { + struct device_node *pins_node; + + pins_node = of_parse_phandle(node, "pinctrl-0", 0); + if (!pins_node) { + printk(KERN_ERR LIRC_DRIVER_NAME + ": pinctrl settings not found!\n"); + ret = -EINVAL; + goto exit_init_port; + } + + read_pin_settings(pins_node); + + of_property_read_u32(node, "rpi,sense", &sense); + + read_bool_property(node, "rpi,softcarrier", &softcarrier); + + read_bool_property(node, "rpi,invert", &invert); + + read_bool_property(node, "rpi,debug", &debug); + + } + else + { + if (gpio_in_pin >= BCM2708_NR_GPIOS || + gpio_out_pin >= BCM2708_NR_GPIOS) { + ret = -EINVAL; + printk(KERN_ERR LIRC_DRIVER_NAME + ": invalid GPIO pin(s) specified!\n"); + goto exit_init_port; + } + + if (gpio_request(gpio_out_pin, LIRC_DRIVER_NAME " ir/out")) { + printk(KERN_ALERT LIRC_DRIVER_NAME + ": cant claim gpio pin %d\n", gpio_out_pin); + ret = -ENODEV; + goto exit_init_port; + } + + if (gpio_request(gpio_in_pin, LIRC_DRIVER_NAME " ir/in")) { + printk(KERN_ALERT LIRC_DRIVER_NAME + ": cant claim gpio pin %d\n", gpio_in_pin); + ret = -ENODEV; + goto exit_gpio_free_out_pin; + } + + bcm2708_gpio_setpull(gpiochip, gpio_in_pin, gpio_in_pull); + gpiochip->direction_input(gpiochip, gpio_in_pin); + gpiochip->direction_output(gpiochip, gpio_out_pin, 1); + } + + gpiochip->set(gpiochip, gpio_out_pin, invert); + + irq_num = gpiochip->to_irq(gpiochip, gpio_in_pin); + dprintk("to_irq %d\n", irq_num); + + /* if pin is high, then this must be an active low receiver. */ + if (sense == -1) { + /* wait 1/2 sec for the power supply */ + msleep(500); + + /* + * probe 9 times every 0.04s, collect "votes" for + * active high/low + */ + nlow = 0; + nhigh = 0; + for (i = 0; i < 9; i++) { + if (gpiochip->get(gpiochip, gpio_in_pin)) + nlow++; + else + nhigh++; + msleep(40); + } + sense = (nlow >= nhigh ? 1 : 0); + printk(KERN_INFO LIRC_DRIVER_NAME + ": auto-detected active %s receiver on GPIO pin %d\n", + sense ? "low" : "high", gpio_in_pin); + } else { + printk(KERN_INFO LIRC_DRIVER_NAME + ": manually using active %s receiver on GPIO pin %d\n", + sense ? "low" : "high", gpio_in_pin); + } + + return 0; + + exit_gpio_free_out_pin: + gpio_free(gpio_out_pin); + + exit_init_port: + return ret; +} + +// called when the character device is opened +static int set_use_inc(void *data) +{ + int result; + + /* initialize timestamp */ + do_gettimeofday(&lasttv); + + result = request_irq(irq_num, + (irq_handler_t) irq_handler, + IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING, + LIRC_DRIVER_NAME, (void*) 0); + + switch (result) { + case -EBUSY: + printk(KERN_ERR LIRC_DRIVER_NAME + ": IRQ %d is busy\n", + irq_num); + return -EBUSY; + case -EINVAL: + printk(KERN_ERR LIRC_DRIVER_NAME + ": Bad irq number or handler\n"); + return -EINVAL; + default: + dprintk("Interrupt %d obtained\n", + irq_num); + break; + }; + + /* initialize pulse/space widths */ + init_timing_params(duty_cycle, freq); + + return 0; +} + +static void set_use_dec(void *data) +{ + /* GPIO Pin Falling/Rising Edge Detect Disable */ + irq_set_irq_type(irq_num, 0); + disable_irq(irq_num); + + free_irq(irq_num, (void *) 0); + + dprintk(KERN_INFO LIRC_DRIVER_NAME + ": freed IRQ %d\n", irq_num); +} + +static ssize_t lirc_write(struct file *file, const char *buf, + size_t n, loff_t *ppos) +{ + int i, count; + unsigned long flags; + long delta = 0; + int *wbuf; + + count = n / sizeof(int); + if (n % sizeof(int) || count % 2 == 0) + return -EINVAL; + wbuf = memdup_user(buf, n); + if (IS_ERR(wbuf)) + return PTR_ERR(wbuf); + spin_lock_irqsave(&lock, flags); + + for (i = 0; i < count; i++) { + if (i%2) + send_space(wbuf[i] - delta); + else + delta = send_pulse(wbuf[i]); + } + gpiochip->set(gpiochip, gpio_out_pin, invert); + + spin_unlock_irqrestore(&lock, flags); + kfree(wbuf); + return n; +} + +static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + int result; + __u32 value; + + switch (cmd) { + case LIRC_GET_SEND_MODE: + return -ENOIOCTLCMD; + break; + + case LIRC_SET_SEND_MODE: + result = get_user(value, (__u32 *) arg); + if (result) + return result; + /* only LIRC_MODE_PULSE supported */ + if (value != LIRC_MODE_PULSE) + return -ENOSYS; + break; + + case LIRC_GET_LENGTH: + return -ENOSYS; + break; + + case LIRC_SET_SEND_DUTY_CYCLE: + dprintk("SET_SEND_DUTY_CYCLE\n"); + result = get_user(value, (__u32 *) arg); + if (result) + return result; + if (value <= 0 || value > 100) + return -EINVAL; + return init_timing_params(value, freq); + break; + + case LIRC_SET_SEND_CARRIER: + dprintk("SET_SEND_CARRIER\n"); + result = get_user(value, (__u32 *) arg); + if (result) + return result; + if (value > 500000 || value < 20000) + return -EINVAL; + return init_timing_params(duty_cycle, value); + break; + + default: + return lirc_dev_fop_ioctl(filep, cmd, arg); + } + return 0; +} + +static const struct file_operations lirc_fops = { + .owner = THIS_MODULE, + .write = lirc_write, + .unlocked_ioctl = lirc_ioctl, + .read = lirc_dev_fop_read, + .poll = lirc_dev_fop_poll, + .open = lirc_dev_fop_open, + .release = lirc_dev_fop_close, + .llseek = no_llseek, +}; + +static struct lirc_driver driver = { + .name = LIRC_DRIVER_NAME, + .minor = -1, + .code_length = 1, + .sample_rate = 0, + .data = NULL, + .add_to_buf = NULL, + .rbuf = &rbuf, + .set_use_inc = set_use_inc, + .set_use_dec = set_use_dec, + .fops = &lirc_fops, + .dev = NULL, + .owner = THIS_MODULE, +}; + +static const struct of_device_id lirc_rpi_of_match[] = { + { .compatible = "rpi,lirc-rpi", }, + {}, +}; +MODULE_DEVICE_TABLE(of, lirc_rpi_of_match); + +static struct platform_driver lirc_rpi_driver = { + .driver = { + .name = LIRC_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(lirc_rpi_of_match), + }, +}; + +static int __init lirc_rpi_init(void) +{ + struct device_node *node; + int result; + + /* Init read buffer. */ + result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN); + if (result < 0) + return -ENOMEM; + + result = platform_driver_register(&lirc_rpi_driver); + if (result) { + printk(KERN_ERR LIRC_DRIVER_NAME + ": lirc register returned %d\n", result); + goto exit_buffer_free; + } + + node = of_find_compatible_node(NULL, NULL, + lirc_rpi_of_match[0].compatible); + + if (node) { + /* DT-enabled */ + lirc_rpi_dev = of_find_device_by_node(node); + WARN_ON(lirc_rpi_dev->dev.of_node != node); + of_node_put(node); + } + else { + lirc_rpi_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0); + if (!lirc_rpi_dev) { + result = -ENOMEM; + goto exit_driver_unregister; + } + + result = platform_device_add(lirc_rpi_dev); + if (result) + goto exit_device_put; + } + + return 0; + + exit_device_put: + platform_device_put(lirc_rpi_dev); + + exit_driver_unregister: + platform_driver_unregister(&lirc_rpi_driver); + + exit_buffer_free: + lirc_buffer_free(&rbuf); + + return result; +} + +static void lirc_rpi_exit(void) +{ + if (!lirc_rpi_dev->dev.of_node) + platform_device_unregister(lirc_rpi_dev); + platform_driver_unregister(&lirc_rpi_driver); + lirc_buffer_free(&rbuf); +} + +static int __init lirc_rpi_init_module(void) +{ + int result; + + result = lirc_rpi_init(); + if (result) + return result; + + result = init_port(); + if (result < 0) + goto exit_rpi; + + driver.features = LIRC_CAN_SET_SEND_DUTY_CYCLE | + LIRC_CAN_SET_SEND_CARRIER | + LIRC_CAN_SEND_PULSE | + LIRC_CAN_REC_MODE2; + + driver.dev = &lirc_rpi_dev->dev; + driver.minor = lirc_register_driver(&driver); + + if (driver.minor < 0) { + printk(KERN_ERR LIRC_DRIVER_NAME + ": device registration failed with %d\n", result); + result = -EIO; + goto exit_rpi; + } + + printk(KERN_INFO LIRC_DRIVER_NAME ": driver registered!\n"); + + return 0; + + exit_rpi: + lirc_rpi_exit(); + + return result; +} + +static void __exit lirc_rpi_exit_module(void) +{ + lirc_unregister_driver(driver.minor); + + gpio_free(gpio_out_pin); + gpio_free(gpio_in_pin); + + lirc_rpi_exit(); + + printk(KERN_INFO LIRC_DRIVER_NAME ": cleaned up module\n"); +} + +module_init(lirc_rpi_init_module); +module_exit(lirc_rpi_exit_module); + +MODULE_DESCRIPTION("Infra-red receiver and blaster driver for Raspberry Pi GPIO."); +MODULE_AUTHOR("Aron Robert Szabo "); +MODULE_AUTHOR("Michael Bishop "); +MODULE_LICENSE("GPL"); + +module_param(gpio_out_pin, int, S_IRUGO); +MODULE_PARM_DESC(gpio_out_pin, "GPIO output/transmitter pin number of the BCM" + " processor. (default 17"); + +module_param(gpio_in_pin, int, S_IRUGO); +MODULE_PARM_DESC(gpio_in_pin, "GPIO input pin number of the BCM processor." + " (default 18"); + +module_param(gpio_in_pull, int, S_IRUGO); +MODULE_PARM_DESC(gpio_in_pull, "GPIO input pin pull configuration." + " (0 = off, 1 = up, 2 = down, default down)"); + +module_param(sense, int, S_IRUGO); +MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit" + " (0 = active high, 1 = active low )"); + +module_param(softcarrier, bool, S_IRUGO); +MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)"); + +module_param(invert, bool, S_IRUGO); +MODULE_PARM_DESC(invert, "Invert output (0 = off, 1 = on, default off"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging messages"); 'n530' href='#n530'>530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900