* [PATCH] Added support for Atheros AR300x Bluetooth Chip
@ 2010-03-15 5:01 suraj
2010-03-24 5:27 ` suraj
` (2 more replies)
0 siblings, 3 replies; 36+ messages in thread
From: suraj @ 2010-03-15 5:01 UTC (permalink / raw)
To: linux-bluetooth; +Cc: marcel, Luis.Rodriguez, Jothikumar.Mothilal
This protocol implements support for power management feature provided by AR300x chip.
This lets the controller chip go to sleep mode if there is no Bluetooth activity for some time.
It then wakes up the chip in case of a Bluetooth activity.
Signed-off-by: Suraj <suraj@atheros.com>
---
drivers/bluetooth/Kconfig | 11 ++
drivers/bluetooth/Makefile | 1 +
drivers/bluetooth/hci_ath.c | 353 +++++++++++++++++++++++++++++++++++++++++
drivers/bluetooth/hci_ldisc.c | 6 +
drivers/bluetooth/hci_uart.h | 8 +-
5 files changed, 378 insertions(+), 1 deletions(-)
create mode 100755 drivers/bluetooth/hci_ath.c
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 058fbcc..81abeff 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -58,6 +58,17 @@ config BT_HCIUART_BCSP
Say Y here to compile support for HCI BCSP protocol.
+config BT_HCIUART_ATH
+ bool "Atheros AR300x Board support"
+ depends on BT_HCIUART
+ help
+ HCIATH (HCI Atheros) is a serial protocol for communication
+ between Bluetooth device and host with support for Atheros AR300x
+ power management feature. This protocol is required for
+ serial Bluetooth devices that are based on Atheros AR300x chips.
+
+ Say Y here to compile support for HCIATH protocol.
+
config BT_HCIUART_LL
bool "HCILL protocol support"
depends on BT_HCIUART
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 7e5aed5..1481faa 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -26,4 +26,5 @@ hci_uart-y := hci_ldisc.o
hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o
hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o
hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o
+hci_uart-$(CONFIG_BT_HCIUART_ATH) += hci_ath.o
hci_uart-objs := $(hci_uart-y)
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
new file mode 100755
index 0000000..13e4404
--- /dev/null
+++ b/drivers/bluetooth/hci_ath.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2009-2010 Atheros Communications Inc.
+ *
+ * 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 <linux/module.h>
+#include <linux/kernel.h>
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+
+/* HCIATH receiver States */
+#define HCIATH_W4_PACKET_TYPE 0
+#define HCIATH_W4_EVENT_HDR 1
+#define HCIATH_W4_ACL_HDR 2
+#define HCIATH_W4_SCO_HDR 3
+#define HCIATH_W4_DATA 4
+
+struct ath_struct {
+ struct hci_uart *hu;
+ unsigned int rx_state;
+ unsigned int rx_count;
+ unsigned int cur_sleep;
+
+ spinlock_t hciath_lock;
+ struct sk_buff *rx_skb;
+ struct sk_buff_head txq;
+ wait_queue_head_t wqevt;
+ struct work_struct ctxtsw;
+};
+
+int ath_wakeup_ar3001(struct tty_struct *tty)
+{
+ struct termios settings;
+ int status = 0x00;
+ mm_segment_t oldfs;
+ status = tty->driver->ops->tiocmget(tty, NULL);
+
+ if ((status & TIOCM_CTS))
+ return status;
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings);
+
+ settings.c_cflag &= ~CRTSCTS;
+ n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings);
+ set_fs(oldfs);
+ status = tty->driver->ops->tiocmget(tty, NULL);
+
+ /* Wake up board */
+ tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS);
+ mdelay(20);
+
+ status = tty->driver->ops->tiocmget(tty, NULL);
+
+ tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00);
+ mdelay(20);
+
+ status = tty->driver->ops->tiocmget(tty, NULL);
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings);
+
+ settings.c_cflag |= CRTSCTS;
+ n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings);
+ set_fs(oldfs);
+ return status;
+}
+
+static void ath_context_switch(struct work_struct *work)
+{
+ int status;
+ struct ath_struct *ath;
+ struct hci_uart *hu;
+ struct tty_struct *tty;
+
+ ath = container_of(work, struct ath_struct, ctxtsw);
+
+ hu = ath->hu;
+ tty = hu->tty;
+
+ /* verify and wake up controller */
+ if (ath->cur_sleep) {
+
+ status = ath_wakeup_ar3001(tty);
+ if (!(status & TIOCM_CTS))
+ return;
+ }
+
+ /* Ready to send Data */
+ clear_bit(HCI_UART_SENDING, &hu->tx_state);
+ hci_uart_tx_wakeup(hu);
+}
+
+int ath_check_sleep_cmd(struct ath_struct *ath, unsigned char *packet)
+{
+ if (packet[0] == 0x04 && packet[1] == 0xFC)
+ ath->cur_sleep = packet[3];
+
+ return 0;
+}
+
+
+/* Initialize protocol */
+static int ath_open(struct hci_uart *hu)
+{
+ struct ath_struct *ath;
+ BT_DBG("hu %p", hu);
+
+ ath = kzalloc(sizeof(*ath), GFP_ATOMIC);
+ if (!ath)
+ return -ENOMEM;
+
+ skb_queue_head_init(&ath->txq);
+ spin_lock_init(&ath->hciath_lock);
+
+ ath->cur_sleep = 0;
+ hu->priv = ath;
+ ath->hu = hu;
+
+ init_waitqueue_head(&ath->wqevt);
+ INIT_WORK(&ath->ctxtsw, ath_context_switch);
+ return 0;
+}
+
+/* Flush protocol data */
+static int ath_flush(struct hci_uart *hu)
+{
+ struct ath_struct *ath = hu->priv;
+ BT_DBG("hu %p", hu);
+ skb_queue_purge(&ath->txq);
+
+ return 0;
+}
+
+/* Close protocol */
+static int ath_close(struct hci_uart *hu)
+{
+ struct ath_struct *ath = hu->priv;
+ BT_DBG("hu %p", hu);
+
+ skb_queue_purge(&ath->txq);
+
+ if (ath->rx_skb)
+ kfree_skb(ath->rx_skb);
+
+ wake_up_interruptible(&ath->wqevt);
+ hu->priv = NULL;
+ kfree(ath);
+ return 0;
+}
+
+/* Enqueue frame for transmittion */
+static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+ struct ath_struct *ath = hu->priv;
+ if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) {
+
+ /* Discard SCO packet.AR3001 does not support SCO over HCI */
+ BT_DBG("SCO Packet over HCI received Dropping\n");
+ kfree(skb);
+ return 0;
+ }
+ BT_DBG("hu %p skb %p", hu, skb);
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+ skb_queue_tail(&ath->txq, skb);
+ set_bit(HCI_UART_SENDING, &hu->tx_state);
+
+ schedule_work(&ath->ctxtsw);
+ return 0;
+}
+
+static struct sk_buff *ath_dequeue(struct hci_uart *hu)
+{
+ struct ath_struct *ath = hu->priv;
+ struct sk_buff *skbuf;
+
+ skbuf = skb_dequeue(&ath->txq);
+ if (skbuf != NULL)
+ ath_check_sleep_cmd(ath, &skbuf->data[1]);
+
+ return skbuf;
+}
+
+static inline int ath_check_data_len(struct ath_struct *ath, int len)
+{
+ register int room = skb_tailroom(ath->rx_skb);
+ BT_DBG("len %d room %d", len, room);
+
+ if (len > room) {
+ BT_ERR("Data length is too large");
+ kfree_skb(ath->rx_skb);
+ ath->rx_state = HCIATH_W4_PACKET_TYPE;
+ ath->rx_skb = NULL;
+ ath->rx_count = 0;
+ } else {
+ ath->rx_state = HCIATH_W4_DATA;
+ ath->rx_count = len;
+ return len;
+ }
+
+ return 0;
+}
+
+/* Recv data */
+static int ath_recv(struct hci_uart *hu, void *data, int count)
+{
+ struct ath_struct *ath = hu->priv;
+ register char *ptr;
+ struct hci_event_hdr *eh;
+ struct hci_acl_hdr *ah;
+ struct hci_sco_hdr *sh;
+ struct sk_buff *skbuf;
+ register int len, type, dlen;
+
+ skbuf = NULL;
+ BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count,
+ ath->rx_state, ath->rx_count);
+ ptr = data;
+ while (count) {
+ if (ath->rx_count) {
+
+ len = min_t(unsigned int, ath->rx_count, count);
+ memcpy(skb_put(ath->rx_skb, len), ptr, len);
+ ath->rx_count -= len;
+ count -= len;
+ ptr += len;
+
+ if (ath->rx_count)
+ continue;
+ switch (ath->rx_state) {
+ case HCIATH_W4_DATA:
+ hci_recv_frame(ath->rx_skb);
+ ath->rx_state = HCIATH_W4_PACKET_TYPE;
+ ath->rx_skb = NULL;
+ ath->rx_count = 0;
+ continue;
+ case HCIATH_W4_EVENT_HDR:
+ eh = (struct hci_event_hdr *)ath->rx_skb->data;
+ BT_DBG("Event header: evt 0x%2.2x plen %d",
+ eh->evt, eh->plen);
+ ath_check_data_len(ath, eh->plen);
+ continue;
+ case HCIATH_W4_ACL_HDR:
+ ah = (struct hci_acl_hdr *)ath->rx_skb->data;
+ dlen = __le16_to_cpu(ah->dlen);
+ BT_DBG("ACL header: dlen %d", dlen);
+ ath_check_data_len(ath, dlen);
+ continue;
+ case HCIATH_W4_SCO_HDR:
+ sh = (struct hci_sco_hdr *)ath->rx_skb->data;
+ BT_DBG("SCO header: dlen %d", sh->dlen);
+ ath_check_data_len(ath, sh->dlen);
+ continue;
+ }
+ }
+
+ /* HCIATH_W4_PACKET_TYPE */
+ switch (*ptr) {
+ case HCI_EVENT_PKT:
+ BT_DBG("Event packet");
+ ath->rx_state = HCIATH_W4_EVENT_HDR;
+ ath->rx_count = HCI_EVENT_HDR_SIZE;
+ type = HCI_EVENT_PKT;
+ break;
+ case HCI_ACLDATA_PKT:
+ BT_DBG("ACL packet");
+ ath->rx_state = HCIATH_W4_ACL_HDR;
+ ath->rx_count = HCI_ACL_HDR_SIZE;
+ type = HCI_ACLDATA_PKT;
+ break;
+ case HCI_SCODATA_PKT:
+ BT_DBG("SCO packet");
+ ath->rx_state = HCIATH_W4_SCO_HDR;
+ ath->rx_count = HCI_SCO_HDR_SIZE;
+ type = HCI_SCODATA_PKT;
+ break;
+ default:
+ BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr);
+ hu->hdev->stat.err_rx++;
+ ptr++;
+ count--;
+ continue;
+ };
+ ptr++;
+ count--;
+
+ /* Allocate packet */
+ ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+ if (!ath->rx_skb) {
+ BT_ERR("Can't allocate mem for new packet");
+ ath->rx_state = HCIATH_W4_PACKET_TYPE;
+ ath->rx_count = 0;
+ return -ENOMEM;
+ }
+ ath->rx_skb->dev = (void *)hu->hdev;
+ bt_cb(ath->rx_skb)->pkt_type = type;
+ } return count;
+}
+
+static struct hci_uart_proto athp = {
+ .id = HCI_UART_ATH,
+ .open = ath_open,
+ .close = ath_close,
+ .recv = ath_recv,
+ .enqueue = ath_enqueue,
+ .dequeue = ath_dequeue,
+ .flush = ath_flush,
+};
+
+int ath_init(void)
+{
+ int err = hci_uart_register_proto(&athp);
+ if (!err)
+ BT_INFO("HCIATH protocol initialized");
+
+ else
+ BT_ERR("HCIATH protocol registration failed");
+ return err;
+}
+
+int ath_deinit(void)
+{
+ return hci_uart_unregister_proto(&athp);
+}
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 76a1abb..7dd76d1 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -542,6 +542,9 @@ static int __init hci_uart_init(void)
#ifdef CONFIG_BT_HCIUART_LL
ll_init();
#endif
+#ifdef CONFIG_BT_HCIUART_ATH
+ ath_init();
+#endif
return 0;
}
@@ -559,6 +562,9 @@ static void __exit hci_uart_exit(void)
#ifdef CONFIG_BT_HCIUART_LL
ll_deinit();
#endif
+#ifdef CONFIG_BT_HCIUART_ATH
+ ath_deinit();
+#endif
/* Release tty registration of line discipline */
if ((err = tty_unregister_ldisc(N_HCI)))
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
index 50113db..385537f 100644
--- a/drivers/bluetooth/hci_uart.h
+++ b/drivers/bluetooth/hci_uart.h
@@ -33,13 +33,14 @@
#define HCIUARTGETDEVICE _IOR('U', 202, int)
/* UART protocols */
-#define HCI_UART_MAX_PROTO 5
+#define HCI_UART_MAX_PROTO 6
#define HCI_UART_H4 0
#define HCI_UART_BCSP 1
#define HCI_UART_3WIRE 2
#define HCI_UART_H4DS 3
#define HCI_UART_LL 4
+#define HCI_UART_ATH 5
struct hci_uart;
@@ -91,3 +92,8 @@ int bcsp_deinit(void);
int ll_init(void);
int ll_deinit(void);
#endif
+
+#ifdef CONFIG_BT_HCIUART_ATH
+int ath_init(void);
+int ath_deinit(void);
+#endif
--
1.6.3.3
^ permalink raw reply related [flat|nested] 36+ messages in thread* Re: [PATCH] Added support for Atheros AR300x Bluetooth Chip 2010-03-15 5:01 [PATCH] Added support for Atheros AR300x Bluetooth Chip suraj @ 2010-03-24 5:27 ` suraj 2010-03-29 9:01 ` suraj 2010-03-31 10:59 ` [PATCH] Added Host level support for Atheros AR3xxx " suraj 2010-04-19 23:53 ` [PATCH] Added support for Atheros AR300x " Gustavo F. Padovan 2010-04-20 10:20 ` [PATCH v3] " suraj 2 siblings, 2 replies; 36+ messages in thread From: suraj @ 2010-03-24 5:27 UTC (permalink / raw) To: linux-bluetooth; +Cc: marcel, Luis.Rodriguez, Jothikumar.Mothilal On Mon, 2010-03-15 at 10:31 +0530, suraj wrote: > This protocol implements support for power management feature provided by AR300x chip. > This lets the controller chip go to sleep mode if there is no Bluetooth activity for some time. > It then wakes up the chip in case of a Bluetooth activity. > > > Signed-off-by: Suraj <suraj@atheros.com> > > --- > drivers/bluetooth/Kconfig | 11 ++ > drivers/bluetooth/Makefile | 1 + > drivers/bluetooth/hci_ath.c | 353 +++++++++++++++++++++++++++++++++++++++++ > drivers/bluetooth/hci_ldisc.c | 6 + > drivers/bluetooth/hci_uart.h | 8 +- > 5 files changed, 378 insertions(+), 1 deletions(-) > create mode 100755 drivers/bluetooth/hci_ath.c > > diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig > index 058fbcc..81abeff 100644 > --- a/drivers/bluetooth/Kconfig > +++ b/drivers/bluetooth/Kconfig > @@ -58,6 +58,17 @@ config BT_HCIUART_BCSP > > Say Y here to compile support for HCI BCSP protocol. > > +config BT_HCIUART_ATH > + bool "Atheros AR300x Board support" > + depends on BT_HCIUART > + help > + HCIATH (HCI Atheros) is a serial protocol for communication > + between Bluetooth device and host with support for Atheros AR300x > + power management feature. This protocol is required for > + serial Bluetooth devices that are based on Atheros AR300x chips. > + > + Say Y here to compile support for HCIATH protocol. > + > config BT_HCIUART_LL > bool "HCILL protocol support" > depends on BT_HCIUART > diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile > index 7e5aed5..1481faa 100644 > --- a/drivers/bluetooth/Makefile > +++ b/drivers/bluetooth/Makefile > @@ -26,4 +26,5 @@ hci_uart-y := hci_ldisc.o > hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o > hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o > hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o > +hci_uart-$(CONFIG_BT_HCIUART_ATH) += hci_ath.o > hci_uart-objs := $(hci_uart-y) > diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c > new file mode 100755 > index 0000000..13e4404 > --- /dev/null > +++ b/drivers/bluetooth/hci_ath.c > @@ -0,0 +1,353 @@ > +/* > + * Copyright (c) 2009-2010 Atheros Communications Inc. > + * > + * 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 <linux/module.h> > +#include <linux/kernel.h> > + > +#include <linux/init.h> > +#include <linux/slab.h> > +#include <linux/tty.h> > +#include <linux/errno.h> > +#include <linux/ioctl.h> > +#include <linux/skbuff.h> > + > +#include <net/bluetooth/bluetooth.h> > +#include <net/bluetooth/hci_core.h> > + > +#include "hci_uart.h" > + > + > +/* HCIATH receiver States */ > +#define HCIATH_W4_PACKET_TYPE 0 > +#define HCIATH_W4_EVENT_HDR 1 > +#define HCIATH_W4_ACL_HDR 2 > +#define HCIATH_W4_SCO_HDR 3 > +#define HCIATH_W4_DATA 4 > + > +struct ath_struct { > + struct hci_uart *hu; > + unsigned int rx_state; > + unsigned int rx_count; > + unsigned int cur_sleep; > + > + spinlock_t hciath_lock; > + struct sk_buff *rx_skb; > + struct sk_buff_head txq; > + wait_queue_head_t wqevt; > + struct work_struct ctxtsw; > +}; > + > +int ath_wakeup_ar3001(struct tty_struct *tty) > +{ > + struct termios settings; > + int status = 0x00; > + mm_segment_t oldfs; > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + if ((status & TIOCM_CTS)) > + return status; > + > + oldfs = get_fs(); > + set_fs(KERNEL_DS); > + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); > + > + settings.c_cflag &= ~CRTSCTS; > + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); > + set_fs(oldfs); > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + /* Wake up board */ > + tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS); > + mdelay(20); > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00); > + mdelay(20); > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + oldfs = get_fs(); > + set_fs(KERNEL_DS); > + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); > + > + settings.c_cflag |= CRTSCTS; > + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); > + set_fs(oldfs); > + return status; > +} > + > +static void ath_context_switch(struct work_struct *work) > +{ > + int status; > + struct ath_struct *ath; > + struct hci_uart *hu; > + struct tty_struct *tty; > + > + ath = container_of(work, struct ath_struct, ctxtsw); > + > + hu = ath->hu; > + tty = hu->tty; > + > + /* verify and wake up controller */ > + if (ath->cur_sleep) { > + > + status = ath_wakeup_ar3001(tty); > + if (!(status & TIOCM_CTS)) > + return; > + } > + > + /* Ready to send Data */ > + clear_bit(HCI_UART_SENDING, &hu->tx_state); > + hci_uart_tx_wakeup(hu); > +} > + > +int ath_check_sleep_cmd(struct ath_struct *ath, unsigned char *packet) > +{ > + if (packet[0] == 0x04 && packet[1] == 0xFC) > + ath->cur_sleep = packet[3]; > + > + return 0; > +} > + > + > +/* Initialize protocol */ > +static int ath_open(struct hci_uart *hu) > +{ > + struct ath_struct *ath; > + BT_DBG("hu %p", hu); > + > + ath = kzalloc(sizeof(*ath), GFP_ATOMIC); > + if (!ath) > + return -ENOMEM; > + > + skb_queue_head_init(&ath->txq); > + spin_lock_init(&ath->hciath_lock); > + > + ath->cur_sleep = 0; > + hu->priv = ath; > + ath->hu = hu; > + > + init_waitqueue_head(&ath->wqevt); > + INIT_WORK(&ath->ctxtsw, ath_context_switch); > + return 0; > +} > + > +/* Flush protocol data */ > +static int ath_flush(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + BT_DBG("hu %p", hu); > + skb_queue_purge(&ath->txq); > + > + return 0; > +} > + > +/* Close protocol */ > +static int ath_close(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + BT_DBG("hu %p", hu); > + > + skb_queue_purge(&ath->txq); > + > + if (ath->rx_skb) > + kfree_skb(ath->rx_skb); > + > + wake_up_interruptible(&ath->wqevt); > + hu->priv = NULL; > + kfree(ath); > + return 0; > +} > + > +/* Enqueue frame for transmittion */ > +static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) > +{ > + struct ath_struct *ath = hu->priv; > + if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { > + > + /* Discard SCO packet.AR3001 does not support SCO over HCI */ > + BT_DBG("SCO Packet over HCI received Dropping\n"); > + kfree(skb); > + return 0; > + } > + BT_DBG("hu %p skb %p", hu, skb); > + > + /* Prepend skb with frame type */ > + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); > + > + skb_queue_tail(&ath->txq, skb); > + set_bit(HCI_UART_SENDING, &hu->tx_state); > + > + schedule_work(&ath->ctxtsw); > + return 0; > +} > + > +static struct sk_buff *ath_dequeue(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + struct sk_buff *skbuf; > + > + skbuf = skb_dequeue(&ath->txq); > + if (skbuf != NULL) > + ath_check_sleep_cmd(ath, &skbuf->data[1]); > + > + return skbuf; > +} > + > +static inline int ath_check_data_len(struct ath_struct *ath, int len) > +{ > + register int room = skb_tailroom(ath->rx_skb); > + BT_DBG("len %d room %d", len, room); > + > + if (len > room) { > + BT_ERR("Data length is too large"); > + kfree_skb(ath->rx_skb); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_skb = NULL; > + ath->rx_count = 0; > + } else { > + ath->rx_state = HCIATH_W4_DATA; > + ath->rx_count = len; > + return len; > + } > + > + return 0; > +} > + > +/* Recv data */ > +static int ath_recv(struct hci_uart *hu, void *data, int count) > +{ > + struct ath_struct *ath = hu->priv; > + register char *ptr; > + struct hci_event_hdr *eh; > + struct hci_acl_hdr *ah; > + struct hci_sco_hdr *sh; > + struct sk_buff *skbuf; > + register int len, type, dlen; > + > + skbuf = NULL; > + BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, > + ath->rx_state, ath->rx_count); > + ptr = data; > + while (count) { > + if (ath->rx_count) { > + > + len = min_t(unsigned int, ath->rx_count, count); > + memcpy(skb_put(ath->rx_skb, len), ptr, len); > + ath->rx_count -= len; > + count -= len; > + ptr += len; > + > + if (ath->rx_count) > + continue; > + switch (ath->rx_state) { > + case HCIATH_W4_DATA: > + hci_recv_frame(ath->rx_skb); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_skb = NULL; > + ath->rx_count = 0; > + continue; > + case HCIATH_W4_EVENT_HDR: > + eh = (struct hci_event_hdr *)ath->rx_skb->data; > + BT_DBG("Event header: evt 0x%2.2x plen %d", > + eh->evt, eh->plen); > + ath_check_data_len(ath, eh->plen); > + continue; > + case HCIATH_W4_ACL_HDR: > + ah = (struct hci_acl_hdr *)ath->rx_skb->data; > + dlen = __le16_to_cpu(ah->dlen); > + BT_DBG("ACL header: dlen %d", dlen); > + ath_check_data_len(ath, dlen); > + continue; > + case HCIATH_W4_SCO_HDR: > + sh = (struct hci_sco_hdr *)ath->rx_skb->data; > + BT_DBG("SCO header: dlen %d", sh->dlen); > + ath_check_data_len(ath, sh->dlen); > + continue; > + } > + } > + > + /* HCIATH_W4_PACKET_TYPE */ > + switch (*ptr) { > + case HCI_EVENT_PKT: > + BT_DBG("Event packet"); > + ath->rx_state = HCIATH_W4_EVENT_HDR; > + ath->rx_count = HCI_EVENT_HDR_SIZE; > + type = HCI_EVENT_PKT; > + break; > + case HCI_ACLDATA_PKT: > + BT_DBG("ACL packet"); > + ath->rx_state = HCIATH_W4_ACL_HDR; > + ath->rx_count = HCI_ACL_HDR_SIZE; > + type = HCI_ACLDATA_PKT; > + break; > + case HCI_SCODATA_PKT: > + BT_DBG("SCO packet"); > + ath->rx_state = HCIATH_W4_SCO_HDR; > + ath->rx_count = HCI_SCO_HDR_SIZE; > + type = HCI_SCODATA_PKT; > + break; > + default: > + BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); > + hu->hdev->stat.err_rx++; > + ptr++; > + count--; > + continue; > + }; > + ptr++; > + count--; > + > + /* Allocate packet */ > + ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); > + if (!ath->rx_skb) { > + BT_ERR("Can't allocate mem for new packet"); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_count = 0; > + return -ENOMEM; > + } > + ath->rx_skb->dev = (void *)hu->hdev; > + bt_cb(ath->rx_skb)->pkt_type = type; > + } return count; > +} > + > +static struct hci_uart_proto athp = { > + .id = HCI_UART_ATH, > + .open = ath_open, > + .close = ath_close, > + .recv = ath_recv, > + .enqueue = ath_enqueue, > + .dequeue = ath_dequeue, > + .flush = ath_flush, > +}; > + > +int ath_init(void) > +{ > + int err = hci_uart_register_proto(&athp); > + if (!err) > + BT_INFO("HCIATH protocol initialized"); > + > + else > + BT_ERR("HCIATH protocol registration failed"); > + return err; > +} > + > +int ath_deinit(void) > +{ > + return hci_uart_unregister_proto(&athp); > +} > diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c > index 76a1abb..7dd76d1 100644 > --- a/drivers/bluetooth/hci_ldisc.c > +++ b/drivers/bluetooth/hci_ldisc.c > @@ -542,6 +542,9 @@ static int __init hci_uart_init(void) > #ifdef CONFIG_BT_HCIUART_LL > ll_init(); > #endif > +#ifdef CONFIG_BT_HCIUART_ATH > + ath_init(); > +#endif > > return 0; > } > @@ -559,6 +562,9 @@ static void __exit hci_uart_exit(void) > #ifdef CONFIG_BT_HCIUART_LL > ll_deinit(); > #endif > +#ifdef CONFIG_BT_HCIUART_ATH > + ath_deinit(); > +#endif > > /* Release tty registration of line discipline */ > if ((err = tty_unregister_ldisc(N_HCI))) > diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h > index 50113db..385537f 100644 > --- a/drivers/bluetooth/hci_uart.h > +++ b/drivers/bluetooth/hci_uart.h > @@ -33,13 +33,14 @@ > #define HCIUARTGETDEVICE _IOR('U', 202, int) > > /* UART protocols */ > -#define HCI_UART_MAX_PROTO 5 > +#define HCI_UART_MAX_PROTO 6 > > #define HCI_UART_H4 0 > #define HCI_UART_BCSP 1 > #define HCI_UART_3WIRE 2 > #define HCI_UART_H4DS 3 > #define HCI_UART_LL 4 > +#define HCI_UART_ATH 5 > > struct hci_uart; > > @@ -91,3 +92,8 @@ int bcsp_deinit(void); > int ll_init(void); > int ll_deinit(void); > #endif > + > +#ifdef CONFIG_BT_HCIUART_ATH > +int ath_init(void); > +int ath_deinit(void); > +#endif Hi Marcel, Can you please give your comments regarding the patch that I have sent you? Regards Suraj ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] Added support for Atheros AR300x Bluetooth Chip 2010-03-24 5:27 ` suraj @ 2010-03-29 9:01 ` suraj [not found] ` <1271673889.19858.4.camel@atheros013-desktop> 2010-03-31 10:59 ` [PATCH] Added Host level support for Atheros AR3xxx " suraj 1 sibling, 1 reply; 36+ messages in thread From: suraj @ 2010-03-29 9:01 UTC (permalink / raw) To: linux-bluetooth; +Cc: marcel, Luis.Rodriguez, Jothikumar.Mothilal On Wed, 2010-03-24 at 10:57 +0530, suraj wrote: > On Mon, 2010-03-15 at 10:31 +0530, suraj wrote: > > This protocol implements support for power management feature provided by AR300x chip. > > This lets the controller chip go to sleep mode if there is no Bluetooth activity for some time. > > It then wakes up the chip in case of a Bluetooth activity. > > > > > > Signed-off-by: Suraj <suraj@atheros.com> > > > > --- > > drivers/bluetooth/Kconfig | 11 ++ > > drivers/bluetooth/Makefile | 1 + > > drivers/bluetooth/hci_ath.c | 353 +++++++++++++++++++++++++++++++++++++++++ > > drivers/bluetooth/hci_ldisc.c | 6 + > > drivers/bluetooth/hci_uart.h | 8 +- > > 5 files changed, 378 insertions(+), 1 deletions(-) > > create mode 100755 drivers/bluetooth/hci_ath.c > > > > diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig > > index 058fbcc..81abeff 100644 > > --- a/drivers/bluetooth/Kconfig > > +++ b/drivers/bluetooth/Kconfig > > @@ -58,6 +58,17 @@ config BT_HCIUART_BCSP > > > > Say Y here to compile support for HCI BCSP protocol. > > > > +config BT_HCIUART_ATH > > + bool "Atheros AR300x Board support" > > + depends on BT_HCIUART > > + help > > + HCIATH (HCI Atheros) is a serial protocol for communication > > + between Bluetooth device and host with support for Atheros AR300x > > + power management feature. This protocol is required for > > + serial Bluetooth devices that are based on Atheros AR300x chips. > > + > > + Say Y here to compile support for HCIATH protocol. > > + > > config BT_HCIUART_LL > > bool "HCILL protocol support" > > depends on BT_HCIUART > > diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile > > index 7e5aed5..1481faa 100644 > > --- a/drivers/bluetooth/Makefile > > +++ b/drivers/bluetooth/Makefile > > @@ -26,4 +26,5 @@ hci_uart-y := hci_ldisc.o > > hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o > > hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o > > hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o > > +hci_uart-$(CONFIG_BT_HCIUART_ATH) += hci_ath.o > > hci_uart-objs := $(hci_uart-y) > > diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c > > new file mode 100755 > > index 0000000..13e4404 > > --- /dev/null > > +++ b/drivers/bluetooth/hci_ath.c > > @@ -0,0 +1,353 @@ > > +/* > > + * Copyright (c) 2009-2010 Atheros Communications Inc. > > + * > > + * 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 <linux/module.h> > > +#include <linux/kernel.h> > > + > > +#include <linux/init.h> > > +#include <linux/slab.h> > > +#include <linux/tty.h> > > +#include <linux/errno.h> > > +#include <linux/ioctl.h> > > +#include <linux/skbuff.h> > > + > > +#include <net/bluetooth/bluetooth.h> > > +#include <net/bluetooth/hci_core.h> > > + > > +#include "hci_uart.h" > > + > > + > > +/* HCIATH receiver States */ > > +#define HCIATH_W4_PACKET_TYPE 0 > > +#define HCIATH_W4_EVENT_HDR 1 > > +#define HCIATH_W4_ACL_HDR 2 > > +#define HCIATH_W4_SCO_HDR 3 > > +#define HCIATH_W4_DATA 4 > > + > > +struct ath_struct { > > + struct hci_uart *hu; > > + unsigned int rx_state; > > + unsigned int rx_count; > > + unsigned int cur_sleep; > > + > > + spinlock_t hciath_lock; > > + struct sk_buff *rx_skb; > > + struct sk_buff_head txq; > > + wait_queue_head_t wqevt; > > + struct work_struct ctxtsw; > > +}; > > + > > +int ath_wakeup_ar3001(struct tty_struct *tty) > > +{ > > + struct termios settings; > > + int status = 0x00; > > + mm_segment_t oldfs; > > + status = tty->driver->ops->tiocmget(tty, NULL); > > + > > + if ((status & TIOCM_CTS)) > > + return status; > > + > > + oldfs = get_fs(); > > + set_fs(KERNEL_DS); > > + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); > > + > > + settings.c_cflag &= ~CRTSCTS; > > + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); > > + set_fs(oldfs); > > + status = tty->driver->ops->tiocmget(tty, NULL); > > + > > + /* Wake up board */ > > + tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS); > > + mdelay(20); > > + > > + status = tty->driver->ops->tiocmget(tty, NULL); > > + > > + tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00); > > + mdelay(20); > > + > > + status = tty->driver->ops->tiocmget(tty, NULL); > > + oldfs = get_fs(); > > + set_fs(KERNEL_DS); > > + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); > > + > > + settings.c_cflag |= CRTSCTS; > > + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); > > + set_fs(oldfs); > > + return status; > > +} > > + > > +static void ath_context_switch(struct work_struct *work) > > +{ > > + int status; > > + struct ath_struct *ath; > > + struct hci_uart *hu; > > + struct tty_struct *tty; > > + > > + ath = container_of(work, struct ath_struct, ctxtsw); > > + > > + hu = ath->hu; > > + tty = hu->tty; > > + > > + /* verify and wake up controller */ > > + if (ath->cur_sleep) { > > + > > + status = ath_wakeup_ar3001(tty); > > + if (!(status & TIOCM_CTS)) > > + return; > > + } > > + > > + /* Ready to send Data */ > > + clear_bit(HCI_UART_SENDING, &hu->tx_state); > > + hci_uart_tx_wakeup(hu); > > +} > > + > > +int ath_check_sleep_cmd(struct ath_struct *ath, unsigned char *packet) > > +{ > > + if (packet[0] == 0x04 && packet[1] == 0xFC) > > + ath->cur_sleep = packet[3]; > > + > > + return 0; > > +} > > + > > + > > +/* Initialize protocol */ > > +static int ath_open(struct hci_uart *hu) > > +{ > > + struct ath_struct *ath; > > + BT_DBG("hu %p", hu); > > + > > + ath = kzalloc(sizeof(*ath), GFP_ATOMIC); > > + if (!ath) > > + return -ENOMEM; > > + > > + skb_queue_head_init(&ath->txq); > > + spin_lock_init(&ath->hciath_lock); > > + > > + ath->cur_sleep = 0; > > + hu->priv = ath; > > + ath->hu = hu; > > + > > + init_waitqueue_head(&ath->wqevt); > > + INIT_WORK(&ath->ctxtsw, ath_context_switch); > > + return 0; > > +} > > + > > +/* Flush protocol data */ > > +static int ath_flush(struct hci_uart *hu) > > +{ > > + struct ath_struct *ath = hu->priv; > > + BT_DBG("hu %p", hu); > > + skb_queue_purge(&ath->txq); > > + > > + return 0; > > +} > > + > > +/* Close protocol */ > > +static int ath_close(struct hci_uart *hu) > > +{ > > + struct ath_struct *ath = hu->priv; > > + BT_DBG("hu %p", hu); > > + > > + skb_queue_purge(&ath->txq); > > + > > + if (ath->rx_skb) > > + kfree_skb(ath->rx_skb); > > + > > + wake_up_interruptible(&ath->wqevt); > > + hu->priv = NULL; > > + kfree(ath); > > + return 0; > > +} > > + > > +/* Enqueue frame for transmittion */ > > +static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) > > +{ > > + struct ath_struct *ath = hu->priv; > > + if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { > > + > > + /* Discard SCO packet.AR3001 does not support SCO over HCI */ > > + BT_DBG("SCO Packet over HCI received Dropping\n"); > > + kfree(skb); > > + return 0; > > + } > > + BT_DBG("hu %p skb %p", hu, skb); > > + > > + /* Prepend skb with frame type */ > > + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); > > + > > + skb_queue_tail(&ath->txq, skb); > > + set_bit(HCI_UART_SENDING, &hu->tx_state); > > + > > + schedule_work(&ath->ctxtsw); > > + return 0; > > +} > > + > > +static struct sk_buff *ath_dequeue(struct hci_uart *hu) > > +{ > > + struct ath_struct *ath = hu->priv; > > + struct sk_buff *skbuf; > > + > > + skbuf = skb_dequeue(&ath->txq); > > + if (skbuf != NULL) > > + ath_check_sleep_cmd(ath, &skbuf->data[1]); > > + > > + return skbuf; > > +} > > + > > +static inline int ath_check_data_len(struct ath_struct *ath, int len) > > +{ > > + register int room = skb_tailroom(ath->rx_skb); > > + BT_DBG("len %d room %d", len, room); > > + > > + if (len > room) { > > + BT_ERR("Data length is too large"); > > + kfree_skb(ath->rx_skb); > > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > > + ath->rx_skb = NULL; > > + ath->rx_count = 0; > > + } else { > > + ath->rx_state = HCIATH_W4_DATA; > > + ath->rx_count = len; > > + return len; > > + } > > + > > + return 0; > > +} > > + > > +/* Recv data */ > > +static int ath_recv(struct hci_uart *hu, void *data, int count) > > +{ > > + struct ath_struct *ath = hu->priv; > > + register char *ptr; > > + struct hci_event_hdr *eh; > > + struct hci_acl_hdr *ah; > > + struct hci_sco_hdr *sh; > > + struct sk_buff *skbuf; > > + register int len, type, dlen; > > + > > + skbuf = NULL; > > + BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, > > + ath->rx_state, ath->rx_count); > > + ptr = data; > > + while (count) { > > + if (ath->rx_count) { > > + > > + len = min_t(unsigned int, ath->rx_count, count); > > + memcpy(skb_put(ath->rx_skb, len), ptr, len); > > + ath->rx_count -= len; > > + count -= len; > > + ptr += len; > > + > > + if (ath->rx_count) > > + continue; > > + switch (ath->rx_state) { > > + case HCIATH_W4_DATA: > > + hci_recv_frame(ath->rx_skb); > > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > > + ath->rx_skb = NULL; > > + ath->rx_count = 0; > > + continue; > > + case HCIATH_W4_EVENT_HDR: > > + eh = (struct hci_event_hdr *)ath->rx_skb->data; > > + BT_DBG("Event header: evt 0x%2.2x plen %d", > > + eh->evt, eh->plen); > > + ath_check_data_len(ath, eh->plen); > > + continue; > > + case HCIATH_W4_ACL_HDR: > > + ah = (struct hci_acl_hdr *)ath->rx_skb->data; > > + dlen = __le16_to_cpu(ah->dlen); > > + BT_DBG("ACL header: dlen %d", dlen); > > + ath_check_data_len(ath, dlen); > > + continue; > > + case HCIATH_W4_SCO_HDR: > > + sh = (struct hci_sco_hdr *)ath->rx_skb->data; > > + BT_DBG("SCO header: dlen %d", sh->dlen); > > + ath_check_data_len(ath, sh->dlen); > > + continue; > > + } > > + } > > + > > + /* HCIATH_W4_PACKET_TYPE */ > > + switch (*ptr) { > > + case HCI_EVENT_PKT: > > + BT_DBG("Event packet"); > > + ath->rx_state = HCIATH_W4_EVENT_HDR; > > + ath->rx_count = HCI_EVENT_HDR_SIZE; > > + type = HCI_EVENT_PKT; > > + break; > > + case HCI_ACLDATA_PKT: > > + BT_DBG("ACL packet"); > > + ath->rx_state = HCIATH_W4_ACL_HDR; > > + ath->rx_count = HCI_ACL_HDR_SIZE; > > + type = HCI_ACLDATA_PKT; > > + break; > > + case HCI_SCODATA_PKT: > > + BT_DBG("SCO packet"); > > + ath->rx_state = HCIATH_W4_SCO_HDR; > > + ath->rx_count = HCI_SCO_HDR_SIZE; > > + type = HCI_SCODATA_PKT; > > + break; > > + default: > > + BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); > > + hu->hdev->stat.err_rx++; > > + ptr++; > > + count--; > > + continue; > > + }; > > + ptr++; > > + count--; > > + > > + /* Allocate packet */ > > + ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); > > + if (!ath->rx_skb) { > > + BT_ERR("Can't allocate mem for new packet"); > > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > > + ath->rx_count = 0; > > + return -ENOMEM; > > + } > > + ath->rx_skb->dev = (void *)hu->hdev; > > + bt_cb(ath->rx_skb)->pkt_type = type; > > + } return count; > > +} > > + > > +static struct hci_uart_proto athp = { > > + .id = HCI_UART_ATH, > > + .open = ath_open, > > + .close = ath_close, > > + .recv = ath_recv, > > + .enqueue = ath_enqueue, > > + .dequeue = ath_dequeue, > > + .flush = ath_flush, > > +}; > > + > > +int ath_init(void) > > +{ > > + int err = hci_uart_register_proto(&athp); > > + if (!err) > > + BT_INFO("HCIATH protocol initialized"); > > + > > + else > > + BT_ERR("HCIATH protocol registration failed"); > > + return err; > > +} > > + > > +int ath_deinit(void) > > +{ > > + return hci_uart_unregister_proto(&athp); > > +} > > diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c > > index 76a1abb..7dd76d1 100644 > > --- a/drivers/bluetooth/hci_ldisc.c > > +++ b/drivers/bluetooth/hci_ldisc.c > > @@ -542,6 +542,9 @@ static int __init hci_uart_init(void) > > #ifdef CONFIG_BT_HCIUART_LL > > ll_init(); > > #endif > > +#ifdef CONFIG_BT_HCIUART_ATH > > + ath_init(); > > +#endif > > > > return 0; > > } > > @@ -559,6 +562,9 @@ static void __exit hci_uart_exit(void) > > #ifdef CONFIG_BT_HCIUART_LL > > ll_deinit(); > > #endif > > +#ifdef CONFIG_BT_HCIUART_ATH > > + ath_deinit(); > > +#endif > > > > /* Release tty registration of line discipline */ > > if ((err = tty_unregister_ldisc(N_HCI))) > > diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h > > index 50113db..385537f 100644 > > --- a/drivers/bluetooth/hci_uart.h > > +++ b/drivers/bluetooth/hci_uart.h > > @@ -33,13 +33,14 @@ > > #define HCIUARTGETDEVICE _IOR('U', 202, int) > > > > /* UART protocols */ > > -#define HCI_UART_MAX_PROTO 5 > > +#define HCI_UART_MAX_PROTO 6 > > > > #define HCI_UART_H4 0 > > #define HCI_UART_BCSP 1 > > #define HCI_UART_3WIRE 2 > > #define HCI_UART_H4DS 3 > > #define HCI_UART_LL 4 > > +#define HCI_UART_ATH 5 > > > > struct hci_uart; > > > > @@ -91,3 +92,8 @@ int bcsp_deinit(void); > > int ll_init(void); > > int ll_deinit(void); > > #endif > > + > > +#ifdef CONFIG_BT_HCIUART_ATH > > +int ath_init(void); > > +int ath_deinit(void); > > +#endif > > Hi Marcel, > > Can you please give your comments regarding the patch that I have sent > you? > > Regards > Suraj Hi marcel, A gentle remainder. Can you please update the above driver to the Linux tree? If you find any issues please let me know so that I can do the needful. Regards Suraj ^ permalink raw reply [flat|nested] 36+ messages in thread
[parent not found: <1271673889.19858.4.camel@atheros013-desktop>]
* Re: Support for Atheros AR300x Bluetooth Chip [not found] ` <1271673889.19858.4.camel@atheros013-desktop> @ 2010-04-19 18:11 ` Luis R. Rodriguez 0 siblings, 0 replies; 36+ messages in thread From: Luis R. Rodriguez @ 2010-04-19 18:11 UTC (permalink / raw) To: Suraj Sumangala, Marcel Holtmann Cc: Luis Rodriguez, Jothikumar Mothilal, linux-bluetooth, mcgrof Some code comments below, Marcel if you have time please see if you have any other feedback for their next iteration as well so that they can incorporate on v3. Suraj, on your v3 patch please use [PATCH v3], you can do this with git format patch by using --subject-prefix="PATCH v3". On Mon, Apr 19, 2010 at 03:44:49AM -0700, Suraj Sumangala wrote: > On Mon, 2010-03-29 at 14:31 +0530, suraj wrote: > > On Wed, 2010-03-24 at 10:57 +0530, suraj wrote: > > > On Mon, 2010-03-15 at 10:31 +0530, suraj wrote: > > > > This protocol implements support for power management feature provided by AR300x chip. > > > > This lets the controller chip go to sleep mode if there is no Bluetooth activity for some time. > > > > It then wakes up the chip in case of a Bluetooth activity. > > > > > > > > > > > > Signed-off-by: Suraj <suraj@atheros.com> Don't indent the SOB, just leave it all the way to the left. > > > > --- > > > > drivers/bluetooth/Kconfig | 11 ++ > > > > drivers/bluetooth/Makefile | 1 + > > > > drivers/bluetooth/hci_ath.c | 353 +++++++++++++++++++++++++++++++++++++++++ > > > > drivers/bluetooth/hci_ldisc.c | 6 + > > > > drivers/bluetooth/hci_uart.h | 8 +- > > > > 5 files changed, 378 insertions(+), 1 deletions(-) > > > > create mode 100755 drivers/bluetooth/hci_ath.c > > > > > > > > diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig > > > > index 058fbcc..81abeff 100644 > > > > --- a/drivers/bluetooth/Kconfig > > > > +++ b/drivers/bluetooth/Kconfig > > > > @@ -58,6 +58,17 @@ config BT_HCIUART_BCSP > > > > > > > > Say Y here to compile support for HCI BCSP protocol. > > > > > > > > +config BT_HCIUART_ATH > > > > + bool "Atheros AR300x Board support" > > > > + depends on BT_HCIUART > > > > + help > > > > + HCIATH (HCI Atheros) is a serial protocol for communication > > > > + between Bluetooth device and host with support for Atheros AR300x > > > > + power management feature. This protocol is required for > > > > + serial Bluetooth devices that are based on Atheros AR300x chips. > > > > + > > > > + Say Y here to compile support for HCIATH protocol. > > > > + > > > > config BT_HCIUART_LL > > > > bool "HCILL protocol support" > > > > depends on BT_HCIUART > > > > diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile > > > > index 7e5aed5..1481faa 100644 > > > > --- a/drivers/bluetooth/Makefile > > > > +++ b/drivers/bluetooth/Makefile > > > > @@ -26,4 +26,5 @@ hci_uart-y := hci_ldisc.o > > > > hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o > > > > hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o > > > > hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o > > > > +hci_uart-$(CONFIG_BT_HCIUART_ATH) += hci_ath.o > > > > hci_uart-objs := $(hci_uart-y) > > > > diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c > > > > new file mode 100755 > > > > index 0000000..13e4404 > > > > --- /dev/null > > > > +++ b/drivers/bluetooth/hci_ath.c > > > > @@ -0,0 +1,353 @@ > > > > +/* > > > > + * Copyright (c) 2009-2010 Atheros Communications Inc. > > > > + * > > > > + * 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 <linux/module.h> > > > > +#include <linux/kernel.h> > > > > + > > > > +#include <linux/init.h> > > > > +#include <linux/slab.h> > > > > +#include <linux/tty.h> > > > > +#include <linux/errno.h> > > > > +#include <linux/ioctl.h> > > > > +#include <linux/skbuff.h> > > > > + > > > > +#include <net/bluetooth/bluetooth.h> > > > > +#include <net/bluetooth/hci_core.h> > > > > + > > > > +#include "hci_uart.h" > > > > + > > > > + > > > > +/* HCIATH receiver States */ > > > > +#define HCIATH_W4_PACKET_TYPE 0 > > > > +#define HCIATH_W4_EVENT_HDR 1 > > > > +#define HCIATH_W4_ACL_HDR 2 > > > > +#define HCIATH_W4_SCO_HDR 3 > > > > +#define HCIATH_W4_DATA 4 > > > > + > > > > +struct ath_struct { > > > > + struct hci_uart *hu; > > > > + unsigned int rx_state; > > > > + unsigned int rx_count; > > > > + unsigned int cur_sleep; > > > > + > > > > + spinlock_t hciath_lock; > > > > + struct sk_buff *rx_skb; > > > > + struct sk_buff_head txq; > > > > + wait_queue_head_t wqevt; > > > > + struct work_struct ctxtsw; > > > > +}; > > > > + > > > > +int ath_wakeup_ar3001(struct tty_struct *tty) > > > > +{ ath_wakeup_ar3001() should be made static > > > > + struct termios settings; > > > > + int status = 0x00; > > > > + mm_segment_t oldfs; > > > > + status = tty->driver->ops->tiocmget(tty, NULL); > > > > + > > > > + if ((status & TIOCM_CTS)) > > > > + return status; > > > > + > > > > + oldfs = get_fs(); > > > > + set_fs(KERNEL_DS); > > > > + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); > > > > + > > > > + settings.c_cflag &= ~CRTSCTS; > > > > + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); > > > > + set_fs(oldfs); > > > > + status = tty->driver->ops->tiocmget(tty, NULL); > > > > + > > > > + /* Wake up board */ > > > > + tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS); > > > > + mdelay(20); > > > > + > > > > + status = tty->driver->ops->tiocmget(tty, NULL); > > > > + > > > > + tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00); > > > > + mdelay(20); > > > > + > > > > + status = tty->driver->ops->tiocmget(tty, NULL); > > > > + oldfs = get_fs(); > > > > + set_fs(KERNEL_DS); > > > > + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); > > > > + > > > > + settings.c_cflag |= CRTSCTS; > > > > + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); > > > > + set_fs(oldfs); > > > > + return status; > > > > +} > > > > + > > > > +static void ath_context_switch(struct work_struct *work) > > > > +{ > > > > + int status; > > > > + struct ath_struct *ath; > > > > + struct hci_uart *hu; > > > > + struct tty_struct *tty; > > > > + > > > > + ath = container_of(work, struct ath_struct, ctxtsw); > > > > + > > > > + hu = ath->hu; > > > > + tty = hu->tty; > > > > + > > > > + /* verify and wake up controller */ > > > > + if (ath->cur_sleep) { > > > > + > > > > + status = ath_wakeup_ar3001(tty); > > > > + if (!(status & TIOCM_CTS)) > > > > + return; > > > > + } > > > > + > > > > + /* Ready to send Data */ > > > > + clear_bit(HCI_UART_SENDING, &hu->tx_state); > > > > + hci_uart_tx_wakeup(hu); > > > > +} > > > > + > > > > +int ath_check_sleep_cmd(struct ath_struct *ath, unsigned char *packet) ath_check_sleep_cmd() should be made static. Also just make it return void, you're not checking the return value. Since its so small why not just fold the code in on ath_dequeue() ? If you're worried about the > 80 lines just do something like: if (skbuf != NULL) if (packet[0] == 0x04 && packet[1] == 0xFC) ath->cur_sleep = packet[3]; } or if (!skbuf) return NULL; /* * Add some comment here explaining why you do this * as I already forgot too. */ if (packet[0] == 0x04 && packet[1] == 0xFC) ath->cur_sleep = packet[3]; return skbuf; Please do add the comments about why this is done. > > > > +{ > > > > + if (packet[0] == 0x04 && packet[1] == 0xFC) > > > > + ath->cur_sleep = packet[3]; > > > > + > > > > + return 0; > > > > +} > > > > + > > > > + > > > > +/* Initialize protocol */ > > > > +static int ath_open(struct hci_uart *hu) > > > > +{ > > > > + struct ath_struct *ath; > > > > + BT_DBG("hu %p", hu); > > > > + > > > > + ath = kzalloc(sizeof(*ath), GFP_ATOMIC); > > > > + if (!ath) > > > > + return -ENOMEM; > > > > + > > > > + skb_queue_head_init(&ath->txq); > > > > + spin_lock_init(&ath->hciath_lock); > > > > + > > > > + ath->cur_sleep = 0; > > > > + hu->priv = ath; > > > > + ath->hu = hu; > > > > + > > > > + init_waitqueue_head(&ath->wqevt); > > > > + INIT_WORK(&ath->ctxtsw, ath_context_switch); > > > > + return 0; > > > > +} > > > > + > > > > +/* Flush protocol data */ > > > > +static int ath_flush(struct hci_uart *hu) > > > > +{ > > > > + struct ath_struct *ath = hu->priv; > > > > + BT_DBG("hu %p", hu); > > > > + skb_queue_purge(&ath->txq); > > > > + > > > > + return 0; > > > > +} > > > > + > > > > +/* Close protocol */ > > > > +static int ath_close(struct hci_uart *hu) > > > > +{ > > > > + struct ath_struct *ath = hu->priv; > > > > + BT_DBG("hu %p", hu); > > > > + > > > > + skb_queue_purge(&ath->txq); > > > > + > > > > + if (ath->rx_skb) > > > > + kfree_skb(ath->rx_skb); > > > > + > > > > + wake_up_interruptible(&ath->wqevt); cancel_work_sync(&ath->wqevt); would wait for it for the scheduled work to finish, how about that instead. The wake up interruptible seems more like a hack here, no? > > > > + hu->priv = NULL; > > > > + kfree(ath); > > > > + return 0; > > > > +} > > > > + > > > > +/* Enqueue frame for transmittion */ > > > > +static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) > > > > +{ > > > > + struct ath_struct *ath = hu->priv; > > > > + if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { > > > > + > > > > + /* Discard SCO packet.AR3001 does not support SCO over HCI */ > > > > + BT_DBG("SCO Packet over HCI received Dropping\n"); No need for \n here > > > > + kfree(skb); > > > > + return 0; > > > > + } > > > > + BT_DBG("hu %p skb %p", hu, skb); > > > > + > > > > + /* Prepend skb with frame type */ > > > > + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); > > > > + > > > > + skb_queue_tail(&ath->txq, skb); > > > > + set_bit(HCI_UART_SENDING, &hu->tx_state); > > > > + > > > > + schedule_work(&ath->ctxtsw); > > > > + return 0; > > > > +} > > > > + > > > > +static struct sk_buff *ath_dequeue(struct hci_uart *hu) > > > > +{ > > > > + struct ath_struct *ath = hu->priv; > > > > + struct sk_buff *skbuf; > > > > + > > > > + skbuf = skb_dequeue(&ath->txq); > > > > + if (skbuf != NULL) > > > > + ath_check_sleep_cmd(ath, &skbuf->data[1]); > > > > + > > > > + return skbuf; > > > > +} > > > > + > > > > +static inline int ath_check_data_len(struct ath_struct *ath, int len) > > > > +{ Remove the inline, and remove the return type, you never check it. Repeat this type of review for every other routine you have. If you are not using the return type just remove the thing. If code is used just once then it will automatically get the inline, if its used more than once on the same routine I am not sure if you get your inline -- but I doubt there is a serious performance penalty here. > > > > + register int room = skb_tailroom(ath->rx_skb); > > > > + BT_DBG("len %d room %d", len, room); > > > > + > > > > + if (len > room) { > > > > + BT_ERR("Data length is too large"); > > > > + kfree_skb(ath->rx_skb); > > > > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > > > > + ath->rx_skb = NULL; > > > > + ath->rx_count = 0; > > > > + } else { > > > > + ath->rx_state = HCIATH_W4_DATA; > > > > + ath->rx_count = len; > > > > + return len; > > > > + } > > > > + > > > > + return 0; > > > > +} > > > > + > > > > +/* Recv data */ > > > > +static int ath_recv(struct hci_uart *hu, void *data, int count) > > > > +{ > > > > + struct ath_struct *ath = hu->priv; > > > > + register char *ptr; > > > > + struct hci_event_hdr *eh; > > > > + struct hci_acl_hdr *ah; > > > > + struct hci_sco_hdr *sh; > > > > + struct sk_buff *skbuf; > > > > + register int len, type, dlen; > > > > + > > > > + skbuf = NULL; > > > > + BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, > > > > + ath->rx_state, ath->rx_count); > > > > + ptr = data; > > > > + while (count) { > > > > + if (ath->rx_count) { > > > > + > > > > + len = min_t(unsigned int, ath->rx_count, count); > > > > + memcpy(skb_put(ath->rx_skb, len), ptr, len); > > > > + ath->rx_count -= len; > > > > + count -= len; > > > > + ptr += len; > > > > + > > > > + if (ath->rx_count) > > > > + continue; > > > > + switch (ath->rx_state) { > > > > + case HCIATH_W4_DATA: > > > > + hci_recv_frame(ath->rx_skb); > > > > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > > > > + ath->rx_skb = NULL; > > > > + ath->rx_count = 0; > > > > + continue; > > > > + case HCIATH_W4_EVENT_HDR: > > > > + eh = (struct hci_event_hdr *)ath->rx_skb->data; > > > > + BT_DBG("Event header: evt 0x%2.2x plen %d", > > > > + eh->evt, eh->plen); > > > > + ath_check_data_len(ath, eh->plen); > > > > + continue; > > > > + case HCIATH_W4_ACL_HDR: > > > > + ah = (struct hci_acl_hdr *)ath->rx_skb->data; > > > > + dlen = __le16_to_cpu(ah->dlen); > > > > + BT_DBG("ACL header: dlen %d", dlen); > > > > + ath_check_data_len(ath, dlen); > > > > + continue; > > > > + case HCIATH_W4_SCO_HDR: > > > > + sh = (struct hci_sco_hdr *)ath->rx_skb->data; > > > > + BT_DBG("SCO header: dlen %d", sh->dlen); > > > > + ath_check_data_len(ath, sh->dlen); > > > > + continue; > > > > + } > > > > + } > > > > + > > > > + /* HCIATH_W4_PACKET_TYPE */ > > > > + switch (*ptr) { > > > > + case HCI_EVENT_PKT: > > > > + BT_DBG("Event packet"); > > > > + ath->rx_state = HCIATH_W4_EVENT_HDR; > > > > + ath->rx_count = HCI_EVENT_HDR_SIZE; > > > > + type = HCI_EVENT_PKT; > > > > + break; > > > > + case HCI_ACLDATA_PKT: > > > > + BT_DBG("ACL packet"); > > > > + ath->rx_state = HCIATH_W4_ACL_HDR; > > > > + ath->rx_count = HCI_ACL_HDR_SIZE; > > > > + type = HCI_ACLDATA_PKT; > > > > + break; > > > > + case HCI_SCODATA_PKT: > > > > + BT_DBG("SCO packet"); > > > > + ath->rx_state = HCIATH_W4_SCO_HDR; > > > > + ath->rx_count = HCI_SCO_HDR_SIZE; > > > > + type = HCI_SCODATA_PKT; > > > > + break; > > > > + default: > > > > + BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); > > > > + hu->hdev->stat.err_rx++; > > > > + ptr++; > > > > + count--; > > > > + continue; > > > > + }; > > > > + ptr++; > > > > + count--; > > > > + > > > > + /* Allocate packet */ > > > > + ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); > > > > + if (!ath->rx_skb) { > > > > + BT_ERR("Can't allocate mem for new packet"); > > > > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > > > > + ath->rx_count = 0; > > > > + return -ENOMEM; > > > > + } > > > > + ath->rx_skb->dev = (void *)hu->hdev; > > > > + bt_cb(ath->rx_skb)->pkt_type = type; > > > > + } return count; Please the return in a new line. This routien can later be cleaned up and split into a a couple of helpers, for now its OK I guess. > > > > +} > > > > + > > > > +static struct hci_uart_proto athp = { > > > > + .id = HCI_UART_ATH, > > > > + .open = ath_open, > > > > + .close = ath_close, > > > > + .recv = ath_recv, > > > > + .enqueue = ath_enqueue, > > > > + .dequeue = ath_dequeue, > > > > + .flush = ath_flush, > > > > +}; > > > > + > > > > +int ath_init(void) > > > > +{ > > > > + int err = hci_uart_register_proto(&athp); > > > > + if (!err) > > > > + BT_INFO("HCIATH protocol initialized"); > > > > + > > > > + else > > > > + BT_ERR("HCIATH protocol registration failed"); > > > > + return err; > > > > +} > > > > + > > > > +int ath_deinit(void) > > > > +{ > > > > + return hci_uart_unregister_proto(&athp); > > > > +} > > > > diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c > > > > index 76a1abb..7dd76d1 100644 > > > > --- a/drivers/bluetooth/hci_ldisc.c > > > > +++ b/drivers/bluetooth/hci_ldisc.c > > > > @@ -542,6 +542,9 @@ static int __init hci_uart_init(void) > > > > #ifdef CONFIG_BT_HCIUART_LL > > > > ll_init(); > > > > #endif > > > > +#ifdef CONFIG_BT_HCIUART_ATH > > > > + ath_init(); > > > > +#endif > > > > > > > > return 0; > > > > } > > > > @@ -559,6 +562,9 @@ static void __exit hci_uart_exit(void) > > > > #ifdef CONFIG_BT_HCIUART_LL > > > > ll_deinit(); > > > > #endif > > > > +#ifdef CONFIG_BT_HCIUART_ATH > > > > + ath_deinit(); > > > > +#endif > > > > > > > > /* Release tty registration of line discipline */ > > > > if ((err = tty_unregister_ldisc(N_HCI))) > > > > diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h > > > > index 50113db..385537f 100644 > > > > --- a/drivers/bluetooth/hci_uart.h > > > > +++ b/drivers/bluetooth/hci_uart.h > > > > @@ -33,13 +33,14 @@ > > > > #define HCIUARTGETDEVICE _IOR('U', 202, int) > > > > > > > > /* UART protocols */ > > > > -#define HCI_UART_MAX_PROTO 5 > > > > +#define HCI_UART_MAX_PROTO 6 > > > > > > > > #define HCI_UART_H4 0 > > > > #define HCI_UART_BCSP 1 > > > > #define HCI_UART_3WIRE 2 > > > > #define HCI_UART_H4DS 3 > > > > #define HCI_UART_LL 4 > > > > +#define HCI_UART_ATH 5 > > > > > > > > struct hci_uart; > > > > > > > > @@ -91,3 +92,8 @@ int bcsp_deinit(void); > > > > int ll_init(void); > > > > int ll_deinit(void); > > > > #endif > > > > + > > > > +#ifdef CONFIG_BT_HCIUART_ATH > > > > +int ath_init(void); > > > > +int ath_deinit(void); > > > > +#endif > > > > > > Hi Marcel, > > > > > > Can you please give your comments regarding the patch that I have sent > > > you? > > > > > > Regards > > > Suraj > > > > Hi marcel, > > > > A gentle remainder. > > > > Can you please update the above driver to the Linux tree? If you find > > any issues please let me know so that I can do the needful. > > > > Regards > > Suraj > > Hi Luis, > > I had sent source code to support Atheros AR300x Bluetooth Chip to > Marcel for sending it upstream before around two month. I don't see it being two months, I see it being one month but one month without feedback is indeed quite a while. In my timeline I see it as: 02-22-2010 - Marcel responded to your first patch version for UART bluetooth 02-22-2010 - Suarj posted resplies to Marcel's questions 03-01-2010 - Suraj poked Marcel for feedback based on Suraj's comments ---------- 03-11-2010 - Suraj posts new V2 patch but its sent busted with tabs/space mixup 03-11-2010 - Marcel asks for a resend ---------- 03-14-2010 - Suaraj posts V3 patch with tabs/spaces fixed 03-23-2010 - First poke to Marcel and list 03-29-2010 - Second poke to Marcel and list 03-31-2010 - Patch for userspace hciattach.c changes posted Please ensure to use [PATCH vX] for each X iteration so that it is clear this is a new iteration. > I am yet to get any response for that from his telling whether it is > approved or rejected even after number of remainder mails. I'd just poke again, every two days until you get a reply :) > Can you please help me on this? I am not sure what should be my next > step. > > Regards > Suraj Marcel, *poke* re: Atheros AR300x Bluetooth Chip To help I've reviewed this patch and the previous comments, I've made some of my own above. Please also install sparse and use it every time you make changes to the kernel and sparse check the code you write. git clone git://git.kernel.org/pub/scm/devel/sparse/chrisl/sparse.git git checkout -b rel-042 v0.4.2 make -j # no need to be root make install Then to sparse check your code do: make C=1 M=drivers/bluetooth/ This would have caught the two static checks I noted above. Luis ^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH] Added Host level support for Atheros AR3xxx Bluetooth Chip 2010-03-24 5:27 ` suraj 2010-03-29 9:01 ` suraj @ 2010-03-31 10:59 ` suraj 1 sibling, 0 replies; 36+ messages in thread From: suraj @ 2010-03-31 10:59 UTC (permalink / raw) To: linux-bluetooth; +Cc: marcel, Luis.Rodriguez, Jothikumar.Mothilal Added support for Atheros AR3xxx Bluetooth chip Implemented feature to download firmware configuration from host. Signed-off-by: Suraj <suraj@atheros.com> --- Makefile.tools | 1 + tools/hciattach.8 | 6 + tools/hciattach.c | 159 +++++++- tools/hciattach.h | 2 + tools/hciattach_ar3k.c | 1228 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1395 insertions(+), 1 deletions(-) create mode 100755 tools/hciattach_ar3k.c diff --git a/Makefile.tools b/Makefile.tools index 2735d68..48cf097 100644 --- a/Makefile.tools +++ b/Makefile.tools @@ -23,6 +23,7 @@ tools_l2ping_LDADD = lib/libbluetooth.la tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \ tools/hciattach_st.c \ tools/hciattach_ti.c \ + tools/hciattach_ar3k.c \ tools/hciattach_tialt.c tools_hciattach_LDADD = lib/libbluetooth.la diff --git a/tools/hciattach.8 b/tools/hciattach.8 index f750222..af648f2 100644 --- a/tools/hciattach.8 +++ b/tools/hciattach.8 @@ -49,6 +49,12 @@ specific identifier. Currently supported types are .B any Unspecified HCI_UART interface, no vendor specific options .TP +.B ar3kalt +Atheros AR3xxx based modules with power management disabled +.TP +.B ar3k +Atheros AR3xxx based modules +.TP .B ericsson Ericsson based modules .TP diff --git a/tools/hciattach.c b/tools/hciattach.c index 364c5ff..3557554 100644 --- a/tools/hciattach.c +++ b/tools/hciattach.c @@ -653,7 +653,156 @@ static int csr(int fd, struct uart_t *u, struct termios *ti) } /* - * Silicon Wave specific initialization + * Atheros AR3xxx specific initialization code with power management disabled. + * Suraj Sumangala <Suraj@Atheros.com> + */ +static int ar3kpost(int fd, struct uart_t *u, struct termios *ti) +{ + int dev_id, dd; + struct timespec tm = {0, 50000}; + int status = 0; + + + dev_id = ioctl(fd, HCIUARTGETDEVICE, 0); + if (dev_id < 0) { + perror("cannot get device id"); + return -1; + } + + + dd = hci_open_dev(dev_id); + if (dd < 0) { + perror("HCI device open failed"); + return -1; + } + sleep(2); + /* send command with Sleep feature disabled */ + hci_send_cmd(dd, OGF_VENDOR_CMD, 0x04, 1, &status); + + nanosleep(&tm, NULL); + hci_close_dev(dd); + + return 0; + +} +/* + * Atheros AR3xxx specific initialization post callback + * with power management enabled + * Suraj Sumangala <Suraj@Atheros.com> + */ +static int ar3kpmpost(int fd, struct uart_t *u, struct termios *ti) +{ + int dev_id, dd; + struct timespec tm = {0, 50000}; + int status = 1; + + + dev_id = ioctl(fd, HCIUARTGETDEVICE, 0); + if (dev_id < 0) { + perror("cannot get device id"); + return -1; + } + + + dd = hci_open_dev(dev_id); + if (dd < 0) { + perror("HCI device open failed"); + return -1; + } + sleep(2); + /* send command with Sleep feature disabled */ + if (hci_send_cmd(dd, OGF_VENDOR_CMD, 0x04, 1, &status) < 0) + perror("sleep enable command not sent"); + + nanosleep(&tm, NULL); + hci_close_dev(dd); + + return 0; +} +/* + * Atheros AR3xxx specific initialization + * Suraj Sumangala <Suraj@Atheros.com> + */ +static int ar3kinit(int fd, struct uart_t *u, struct termios *ti) +{ + struct timespec tm = { 0, 500000 }; + unsigned char cmd[14], rsp[100]; + int r; + int baud; + + /* Download PS and patch */ + r = ath_ps_download(fd); + if (r < 0) { + perror("Failed to Download configuration"); + return -1; + } + /* Write BDADDR if user has provided any */ + if (u->bdaddr != NULL) { + /* Set BD_ADDR */ + memset(cmd, 0, sizeof(cmd)); + memset(rsp, 0, sizeof(rsp)); + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x0B; + cmd[2] = 0xfc; + cmd[3] = 0x0A; + cmd[4] = 0x01; + cmd[5] = 0x01; + cmd[6] = 0x00; + cmd[7] = 0x06; + str2ba(u->bdaddr, (bdaddr_t *) (cmd + 8)); + + /* Send command */ + if (write(fd, cmd, 14) != 14) { + fprintf(stderr, "Failed to write BD_ADDR command\n"); + return -1; + } + + /* Read reply */ + if (read_hci_event(fd, rsp, 10) < 0) { + fprintf(stderr, "Failed to set BD_ADDR\n"); + return -1; + } + } + + /* Send HCI Reset to write the configuration */ + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x03; + cmd[2] = 0x0c; + cmd[3] = 0x00; + /* Send reset command */ + r = write(fd, cmd, 4); + + if (r != 4) + return -1; + + nanosleep(&tm, NULL); + if (read_hci_event(fd, rsp, sizeof(rsp)) < 0) + return -1; + + /* Set baud rate command, + * set controller baud rate to user specified value */ + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x0C; + cmd[2] = 0xfc; + cmd[3] = 0x02; + baud = u->speed/100; + cmd[4] = (char)baud; + cmd[5] = (char)(baud >> 8); + + if (write(fd, cmd, 6) != 6) { + perror("Failed to write init command"); + return -1; + } + + /* Wait for the command complete event for Baud rate change Command */ + nanosleep(&tm, NULL); + if (read_hci_event(fd, rsp, sizeof(rsp)) < 0) + return -1; + + return 0; +} +/* + * Silicon Wave specific initialization * Thomas Moser <thomas.moser@tmoser.ch> */ static int swave(int fd, struct uart_t *u, struct termios *ti) @@ -1071,6 +1220,14 @@ struct uart_t uart[] = { /* Broadcom BCM2035 */ { "bcm2035", 0x0A5C, 0x2035, HCI_UART_H4, 115200, 460800, FLOW_CTL, NULL, bcm2035 }, + /* ATHEROS AR3xxx */ + { "ar3kalt", 0x0000, 0x0000, HCI_UART_ATH, + 115200, 115200, FLOW_CTL, NULL, ar3kinit, ar3kpost }, + + { "ar3k", 0x0000, 0x0000, HCI_UART_ATH, + 115200, 115200, FLOW_CTL, NULL, ar3kinit, ar3kpmpost }, + + { NULL, 0 } }; diff --git a/tools/hciattach.h b/tools/hciattach.h index 867563b..5b68668 100644 --- a/tools/hciattach.h +++ b/tools/hciattach.h @@ -36,6 +36,7 @@ #define HCI_UART_3WIRE 2 #define HCI_UART_H4DS 3 #define HCI_UART_LL 4 +#define HCI_UART_ATH 5 int read_hci_event(int fd, unsigned char* buf, int size); int set_speed(int fd, struct termios *ti, int speed); @@ -45,3 +46,4 @@ int texas_post(int fd, struct termios *ti); int texasalt_init(int fd, int speed, struct termios *ti); int stlc2500_init(int fd, bdaddr_t *bdaddr); int bgb2xx_init(int dd, bdaddr_t *bdaddr); +int ath_ps_download(int fd); diff --git a/tools/hciattach_ar3k.c b/tools/hciattach_ar3k.c new file mode 100755 index 0000000..ca418d9 --- /dev/null +++ b/tools/hciattach_ar3k.c @@ -0,0 +1,1228 @@ +/* + * Copyright (c) 2009-2010 Atheros Communications Inc. + * + * 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <ctype.h> +#include <termios.h> +#include <time.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> + +#include "hciattach.h" + +/* Helper data type declaration */ + +#define FALSE 0 +#define TRUE 1 + +/* The maximum number of bytes possible in a patch entry */ +#define MAX_PATCH_SIZE 20000 + +/* Maximum HCI packets that will be formed from the Patch file */ +#define MAX_NUM_PATCH_ENTRY ((MAX_PATCH_SIZE/MAX_BYTE_LENGTH) + 1) + +#define FPGA_REGISTER 0x4FFC + +#define PS_ASIC_FILE "PS_ASIC.pst" +#define PS_FPGA_FILE "PS_FPGA.pst" +#define FW_PATH "/lib/firmware/" +#define PATCH_FILE "RamPatch.txt" +#define BDADDR_FILE "ar3kbdaddr.pst" + + + +#define PS_RESET 2 +#define PS_WRITE 1 +#define WRITE_PATCH 8 +#define PS_VERIFY_CRC 9 +#define ENABLE_PATCH 11 + + +#define NUM_WAKEUP_RETRY 10 + +#define HCI_CMD_HEADER_LEN 7 + +#define RAM_PS_REGION (1<<0) +#define RAM_PATCH_REGION (1<<1) + +#define RAMPS_MAX_PS_TAGS_PER_FILE 50 +#define PS_MAX_LEN 500 +#define LINE_SIZE_MAX (PS_MAX_LEN * 2) + +/* Constant values used by parser */ +#define BYTES_OF_PS_DATA_PER_LINE 16 + +#define MAX_BYTE_LENGTH 244 + +#define skip_space(str) while (*(str) == (' ')) ((str)++) + +#define IS_BETWEEN(x, lower, upper) (((lower) <= (x)) && ((x) <= (upper))) + +#define tohexval(c) (isdigit(c) ? ((c) - '0') : \ + (IS_BETWEEN((c), 'A', 'Z') ? \ + ((c) - 'A' + 10) : ((c) - 'a' + 10))) + +enum ps_entry_type { + hex_type, + decimal_type +}; + +struct ps_tag_entry { + uint32_t tag_id; + uint32_t tag_len; + uint8_t *tag_data; +}; + +struct ps_ram_patch { + int16_t Len; + uint8_t *Data; +}; +struct ps_data_format { + enum ps_entry_type data_type; + unsigned char is_array; +}; + +struct ps_cmd_packet { + uint8_t *Hcipacket; + int packetLen; +}; + +struct st_read_status { + unsigned section; + unsigned line_count; + unsigned char_cnt; + unsigned byte_count; +}; + +/* Stores the number of PS Tags */ +static uint32_t tag_count; + +/* Stores the number of patch commands */ +static uint32_t patch_count; +static uint32_t total_tag_len; +static uint32_t rom_version; +static uint32_t build_version; + +struct ps_tag_entry ps_tag_entry[RAMPS_MAX_PS_TAGS_PER_FILE]; +struct ps_ram_patch ram_patch[MAX_NUM_PATCH_ENTRY]; + + +/* PS parser helper function */ +static void load_hci_header(uint8_t *hci_ps_cmd, + uint8_t opcode, int length, int index) +{ + hci_ps_cmd[0] = 0x0B; + hci_ps_cmd[1] = 0xFC; + hci_ps_cmd[2] = length + 4; + hci_ps_cmd[3] = opcode; + hci_ps_cmd[4] = (index & 0xFF); + hci_ps_cmd[5] = ((index >> 8) & 0xFF); + hci_ps_cmd[6] = length; +} + + +static int ath_create_ps_command(uint8_t opcode, uint32_t param_1, + struct ps_cmd_packet *ps_patch_packet, + uint32_t *index) +{ + uint8_t *hci_ps_cmd; + uint32_t len; + int i; + + hci_ps_cmd = NULL; + + switch (opcode) { + + case WRITE_PATCH: + + for (i = 0; i < param_1; i++) { + /* Allocate command buffer */ + hci_ps_cmd = + (uint8_t *) malloc(ram_patch[i].Len + + HCI_CMD_HEADER_LEN); + + if (hci_ps_cmd == NULL) + return -1; + + memset(hci_ps_cmd, 0, + ram_patch[i].Len + HCI_CMD_HEADER_LEN); + + /* Update commands to buffer */ + load_hci_header(hci_ps_cmd, opcode, ram_patch[i].Len, + i); + memcpy(ram_patch[i].Data, + &hci_ps_cmd[HCI_CMD_HEADER_LEN], + ram_patch[i].Len); + + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; + ps_patch_packet[*index].packetLen = + ram_patch[i].Len + HCI_CMD_HEADER_LEN; + + (*index)++; + } + break; + case ENABLE_PATCH: + + len = 0; + i = 0; + + hci_ps_cmd = (uint8_t *) malloc(len + HCI_CMD_HEADER_LEN); + if (hci_ps_cmd == NULL) + return -1; + + memset(hci_ps_cmd, 0, len + HCI_CMD_HEADER_LEN); + + load_hci_header(hci_ps_cmd, opcode, len, i); + + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; + ps_patch_packet[*index].packetLen + = len + HCI_CMD_HEADER_LEN; + + (*index)++; + + break; + case PS_RESET: + + len = 0x06; + i = 0; + + hci_ps_cmd = (uint8_t *) malloc(len + HCI_CMD_HEADER_LEN); + if (hci_ps_cmd == NULL) + return -1; + + memset(hci_ps_cmd, 0, len + HCI_CMD_HEADER_LEN); + + load_hci_header(hci_ps_cmd, opcode, len, i); + + hci_ps_cmd[7] = 0x00; + hci_ps_cmd[len + HCI_CMD_HEADER_LEN - 2] = (param_1 & 0xFF); + hci_ps_cmd[len + HCI_CMD_HEADER_LEN - 1] = + ((param_1 >> 8) & 0xFF); + + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; + ps_patch_packet[*index].packetLen + = len + HCI_CMD_HEADER_LEN; + + (*index)++; + + break; + case PS_WRITE: + for (i = 0; i < param_1; i++) { + hci_ps_cmd = + (uint8_t *) malloc(ps_tag_entry[i].tag_len + + HCI_CMD_HEADER_LEN); + if (hci_ps_cmd == NULL) + return -1; + + memset(hci_ps_cmd, 0, + ps_tag_entry[i].tag_len + HCI_CMD_HEADER_LEN); + + load_hci_header(hci_ps_cmd, opcode, + ps_tag_entry[i].tag_len, + ps_tag_entry[i].tag_id); + memcpy(&hci_ps_cmd[HCI_CMD_HEADER_LEN], + ps_tag_entry[i].tag_data, + ps_tag_entry[i].tag_len); + + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; + + ps_patch_packet[*index].packetLen = + ps_tag_entry[i].tag_len + HCI_CMD_HEADER_LEN; + + (*index)++; + } + break; + case PS_VERIFY_CRC: + + len = 0x0; + + hci_ps_cmd = (uint8_t *) malloc(len + HCI_CMD_HEADER_LEN); + if (hci_ps_cmd == NULL) + return -1; + + memset(hci_ps_cmd, 0, len + HCI_CMD_HEADER_LEN); + + load_hci_header(hci_ps_cmd, opcode, len, param_1); + + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; + ps_patch_packet[*index].packetLen + = len + HCI_CMD_HEADER_LEN; + + (*index)++; + + break; + default: + break; + } + return 0; +} + +unsigned int get_input_data_format(char *line, + struct ps_data_format *pst_format) +{ + if (line[0] != '[') { + pst_format->data_type = hex_type; + pst_format->is_array = TRUE; + return 0; + } + + switch (line[1]) { + + case 'H': + case 'h': + if (line[2] == ':') { + if ((line[3] == 'a') || (line[3] == 'A')) { + + if (line[4] == ']') { + pst_format->data_type = hex_type; + pst_format->is_array = TRUE; + line += 5; + return 0; + } else + return 1; + + } + + if ((line[3] == 'S') || (line[3] == 's')) { + + if (line[4] == ']') { + pst_format->data_type = hex_type; + pst_format->is_array = FALSE; + line += 5; + return 0; + } else + return 1; + + } else if (line[3] == ']') { + pst_format->data_type = hex_type; + pst_format->is_array = TRUE; + line += 4; + return 0; + } else + return 1; + + } else if (line[2] == ']') { + pst_format->data_type = hex_type; + pst_format->is_array = TRUE; + line += 3; + + return 0; + } else + return 1; + + break; + + case 'A': + case 'a': + + if (line[2] == ':') { + + if ((line[3] == 'h') || (line[3] == 'H')) { + if (line[4] == ']') { + pst_format->data_type = hex_type; + pst_format->is_array = TRUE; + line += 5; + return 0; + } else + return 1; + } else if (line[3] == ']') { + pst_format->data_type = hex_type; + pst_format->is_array = TRUE; + line += 4; + return 0; + } else + return 1; + + } else if (line[2] == ']') { + + pst_format->data_type = hex_type; + pst_format->is_array = TRUE; + line += 3; + return 0; + + } else + return 1; + + break; + + case 'S': + case 's': + if (line[2] == ':') { + + if ((line[3] == 'h') || (line[3] == 'H')) { + + if (line[4] == ']') { + pst_format->data_type = hex_type; + pst_format->is_array = TRUE; + line += 5; + return 0; + } else + return 1; + } else if (line[3] == ']') { + pst_format->data_type = hex_type; + pst_format->is_array = TRUE; + line += 4; + return 0; + } else + return 1; + + } else if (line[2] == ']') { + pst_format->data_type = hex_type; + pst_format->is_array = TRUE; + line += 3; + return 0; + } else + return 1; + break; + default: + return 1; + } +} + +static unsigned int read_data_in_section(char *line, + struct ps_data_format format_info) +{ + char *token_ptr = line; + + if (token_ptr[0] == '[') { + while (token_ptr[0] != ']' && token_ptr[0] != '\0') + token_ptr++; + + if (token_ptr[0] == '\0') + return 0x0FFF; + + token_ptr++; + } + + if (format_info.data_type == hex_type) { + if (format_info.is_array == TRUE) + return 0x0FFF; + else + return strtol(token_ptr, NULL, 16); + } else + return 0x0FFF; +} + +static int ath_parse_file(FILE *stream) +{ + char *buffer; + char *line; + uint8_t tag_cnt; + int16_t byte_count; + uint32_t pos; + int read_count; + struct ps_data_format stps_data_format; + + int line_read = 0; + struct st_read_status read_status = { + 0, 0, 0, 0 + }; + + pos = 0; + buffer = NULL; + tag_cnt = 0; + byte_count = 0; + + if (stream == NULL) { + perror("Could not open config file .\n"); + return -1; + } + + buffer = malloc(LINE_SIZE_MAX + 1); + if (NULL == buffer) + return -1; + + while ((line = fgets(buffer, LINE_SIZE_MAX, stream)) != NULL) { + skip_space(line); + + if ((line[0] == '/') && (line[1] == '/')) + continue; + + if ((line[0] == '#')) { + if (read_status.section != 0) { + perror("error\n"); + if (buffer != NULL) + free(buffer); + return -1; + } else { + read_status.section = 1; + continue; + } + } + + if ((line[0] == '/') && (line[1] == '*')) { + + line += 2; + skip_space(line); + line_read = 0; + read_status.section = 0; + + continue; + } + + if (read_status.section == 1) { + skip_space(line); + + if (get_input_data_format(line, &stps_data_format)) { + if (buffer != NULL) + free(buffer); + return -1; + } + + ps_tag_entry[tag_cnt].tag_id = + read_data_in_section(line, stps_data_format); + read_status.section = 2; + + } else if (read_status.section == 2) { + + if (get_input_data_format(line, &stps_data_format)) { + if (buffer != NULL) + free(buffer); + return -1; + } + + byte_count = + read_data_in_section(line, stps_data_format); + + if (byte_count > LINE_SIZE_MAX / 2) { + if (buffer != NULL) + free(buffer); + + return -1; + } + + ps_tag_entry[tag_cnt].tag_len = byte_count; + ps_tag_entry[tag_cnt].tag_data = (uint8_t *) + malloc(byte_count); + + read_status.section = 3; + read_status.line_count = 0; + + } else if (read_status.section == 3) { + + if (read_status.line_count == 0) { + if (get_input_data_format(line, + &stps_data_format)) { + if (buffer != NULL) + free(buffer); + return -1; + } + } + + skip_space(line); + read_status.char_cnt = 0; + if (line[read_status.char_cnt] == '[') { + + while (line[read_status.char_cnt] != ']' && + line[read_status.char_cnt] != '\0') + read_status.char_cnt++; + + if (line[read_status.char_cnt] == ']') + read_status.char_cnt++; + else + read_status.char_cnt = 0; + + } + + read_count = (byte_count > BYTES_OF_PS_DATA_PER_LINE) + ? BYTES_OF_PS_DATA_PER_LINE : byte_count; + + if ((stps_data_format.data_type == hex_type) + && stps_data_format.is_array == TRUE) { + + while (read_count > 0) { + + ps_tag_entry[tag_cnt].tag_data + [read_status.byte_count] + = (uint8_t)(tohexval + (line[read_status.char_cnt]) + << 4) + | (uint8_t)(tohexval + (line[read_status.char_cnt + 1])); + + ps_tag_entry[tag_cnt].tag_data + [read_status.byte_count + + 1] = (uint8_t)(tohexval + (line[read_status.char_cnt + 3]) + << 4) + | (uint8_t)(tohexval + (line[read_status.char_cnt + 4])); + + read_status.char_cnt += 6; + read_status.byte_count += 2; + read_count -= 2; + + } + + if (byte_count > BYTES_OF_PS_DATA_PER_LINE) + byte_count -= BYTES_OF_PS_DATA_PER_LINE; + else + byte_count = 0; + } + + read_status.line_count++; + + if (byte_count == 0) { + read_status.section = 0; + read_status.char_cnt = 0; + read_status.line_count = 0; + read_status.byte_count = 0; + } else + read_status.char_cnt = 0; + + if ((read_status.section == 0) && + (++tag_cnt == RAMPS_MAX_PS_TAGS_PER_FILE)) { + if (buffer != NULL) + free(buffer); + return -1; + } + + } + line_read++; + } + + tag_count = tag_cnt; + + if (tag_cnt > RAMPS_MAX_PS_TAGS_PER_FILE) { + if (buffer != NULL) + free(buffer); + return -1; + } + + if (buffer != NULL) + free(buffer); + + return 0; +} + +static int parse_patch_file(FILE *stream) +{ + char byte[3]; + char line[MAX_BYTE_LENGTH + 1]; + int byte_cnt, byte_cnt_org; + int count; + int i, j, k; + int data; + uint32_t filepos; + + byte[2] = '\0'; + j = 0; + filepos = 0; + + while (NULL != fgets(line, MAX_BYTE_LENGTH, stream)) { + if (strlen(line) <= 1 || !isxdigit(line[0])) + continue; + else + break; + } + + byte_cnt = strtol(line, NULL, 16); + byte_cnt_org = byte_cnt; + + while (byte_cnt > MAX_BYTE_LENGTH) { + + /* Handle case when the number of patch buffer is + * more than the 20K */ + if (MAX_NUM_PATCH_ENTRY == patch_count) { + for (i = 0; i < patch_count; i++) + free(ram_patch[i].Data); + return -1; + } + ram_patch[patch_count].Len = MAX_BYTE_LENGTH; + ram_patch[patch_count].Data = + (uint8_t *) malloc(MAX_BYTE_LENGTH); + + patch_count++; + byte_cnt = byte_cnt - MAX_BYTE_LENGTH; + } + + ram_patch[patch_count].Len = (byte_cnt & 0xFF); + + if (byte_cnt != 0) { + ram_patch[patch_count].Data = (uint8_t *) malloc(byte_cnt); + patch_count++; + } + + count = 0; + + while (byte_cnt_org > MAX_BYTE_LENGTH) { + for (i = 0, k = 0; i < MAX_BYTE_LENGTH * 2; + i += 2, k++, count += 2) { + if (fgets(byte, 2, stream) == NULL) + return -1; + data = strtoul(&byte[0], NULL, 16); + ram_patch[j].Data[k] = (data & 0xFF); + } + + j++; + byte_cnt_org = byte_cnt_org - MAX_BYTE_LENGTH; + } + + if (j == 0) + j++; + + for (k = 0; k < byte_cnt_org; i += 2, k++, count += 2) { + + if (fgets(byte, 2, stream) == NULL) + return -1; + + data = strtoul(byte, NULL, 16); + ram_patch[j].Data[k] = (data & 0xFF); + } + + return 0; +} + +static int ath_parse_ps(FILE *stream) +{ + int status; + int i; + unsigned char bdaddr_present = 0; + status = -1; + + + if (NULL != stream) + status = ath_parse_file(stream); + + if (tag_count == 0) + total_tag_len = 10; + else { + + for (i = 0; i < tag_count; i++) { + + if (ps_tag_entry[i].tag_id == 1) + bdaddr_present = 1; + if (ps_tag_entry[i].tag_len % 2 == 1) + total_tag_len = total_tag_len + + ps_tag_entry[i].tag_len + 1; + else + total_tag_len = + total_tag_len + ps_tag_entry[i].tag_len; + + } + } + if (tag_count > 0 && !bdaddr_present) + total_tag_len = total_tag_len + 10; + + total_tag_len = total_tag_len + 10 + (tag_count * 4); + + return status; +} + +static int ath_create_cmd_list(struct ps_cmd_packet **hci_packet_list, + uint32_t *num_packets) +{ + uint8_t count; + uint32_t num_cmd_entry = 0; + uint32_t crc = 0; + + *num_packets = 0; + + if (patch_count > 0) + crc |= RAM_PATCH_REGION; + if (tag_count > 0) + crc |= RAM_PS_REGION; + + if (patch_count || tag_count) { + + /* CRC Packet + PS Reset Packet + Patch List + PS List */ + num_cmd_entry += (2 + patch_count + tag_count); + if (patch_count > 0) + num_cmd_entry++; /* Patch Enable Command */ + + (*hci_packet_list) = + malloc(sizeof(struct ps_cmd_packet) * num_cmd_entry); + if (NULL == *hci_packet_list) + perror("memory allocation failed \r\n"); + + ath_create_ps_command(PS_VERIFY_CRC, crc, *hci_packet_list, + num_packets); + + if (patch_count > 0) { + + ath_create_ps_command(WRITE_PATCH, patch_count, + *hci_packet_list, num_packets); + ath_create_ps_command(ENABLE_PATCH, 0, + *hci_packet_list, + num_packets); + + } + + ath_create_ps_command(PS_RESET, total_tag_len, + *hci_packet_list, num_packets); + + if (tag_count > 0) + ath_create_ps_command(PS_WRITE, tag_count, + *hci_packet_list, num_packets); + } + + for (count = 0; count < patch_count; count++) + free(ram_patch[patch_count].Data); + + for (count = 0; count < tag_count; count++) + free(ps_tag_entry[count].tag_data); + + return *num_packets; +} + +static int ath_free_command_list(struct ps_cmd_packet **hci_packet_list, + uint32_t num_packets) +{ + int i; + + if (*hci_packet_list == NULL) + return -1; + + for (i = 0; i < num_packets; i++) + free((*hci_packet_list)[i].Hcipacket); + + free(*hci_packet_list); + + return 0; +} + +static int wake_up_ar3k(int fd) +{ + struct termios ti; + int status; + int retrycount = 0; + if (tcgetattr(fd, &ti) < 0) { + perror("Can't get port settings"); + return -1; + } + ioctl(fd, TIOCMGET, &status); + if (status & TIOCM_CTS) + return 0; + + ti.c_cflag &= ~CRTSCTS; + if (tcsetattr(fd, TCSANOW, &ti) < 0) { + perror("Can't set port settings"); + return -1; + } + + do { + ioctl(fd, TIOCMGET, &status); + + /* deassert */ + status &= (~TIOCM_RTS); + ioctl(fd, TIOCMSET, &status); + + /* read */ + ioctl(fd, TIOCMGET, &status); + + /* assert */ + status |= (TIOCM_RTS); + ioctl(fd, TIOCMSET, &status); + usleep(200); + + /* read */ + ioctl(fd, TIOCMGET, &status); + retrycount++; + if (retrycount > NUM_WAKEUP_RETRY) + break; + } while (!(status & TIOCM_CTS)); + if (!(status & TIOCM_CTS)) + return -1; + + ti.c_cflag |= CRTSCTS; + if (tcsetattr(fd, TCSANOW, &ti) < 0) { + perror("Can't set port settings"); + return -1; + } + return 0; +} + +/* + * This API is used to send the HCI command to controller and return + * with a HCI Command Complete event. + */ +static int send_hci_cmd_wait_event(int dev, + uint8_t *hci_command, + int cmd_length, + uint8_t **event, uint8_t **buffer_to_free) +{ + int r; + uint8_t *hci_event; + uint8_t pkt_type = 0x01; + + if (cmd_length == 0) + return -1; + + /* + * Try to wake up the board if it is asleep + * The assumption here is that the board is asleep. + */ + if (wake_up_ar3k(dev) < 0) + return -1; + + if (write(dev, &pkt_type, 1) != 1) + return -1; + + if (write(dev, (unsigned char *)hci_command, cmd_length) != cmd_length) + return -1; + + hci_event = (uint8_t *) malloc(100); + r = read_hci_event(dev, (unsigned char *)hci_event, 100); + if (r > 0) { + *event = hci_event; + *buffer_to_free = hci_event; + } else { + + /* Did not get an event from controller. return error */ + free(hci_event); + *buffer_to_free = NULL; + return -1; + } + return 0; +} + +static int read_ps_event(uint8_t *data) +{ + + if (data[5] == 0xFC && data[6] == 0x00) { + switch (data[4]) { + case 0x0B: + return 0; + break; + case 0x0C: + + /* Change Baudrate */ + return 0; + break; + case 0x04: + return 0; + break; + case 0x1E: + rom_version = data[10]; + rom_version = ((rom_version << 8) | data[9]); + rom_version = ((rom_version << 8) | data[8]); + rom_version = ((rom_version << 8) | data[7]); + build_version = data[14]; + build_version = ((build_version << 8) | data[13]); + build_version = ((build_version << 8) | data[12]); + build_version = ((build_version << 8) | data[11]); + return 0; + break; + } + } + return -1; +} + +static int get_ps_file_name(int devtype, char *path) +{ + char *filename; + int status = 0; + + if (devtype == 0xdeadc0de) { + filename = PS_ASIC_FILE; + status = 1; + } else { + filename = PS_FPGA_FILE; + status = 0; + } + + sprintf(path, "%s%s", FW_PATH, filename); + return status; +} + +static int get_patch_file_name(int dev_type, int rom_version, + int build_version, char *path) +{ + if ((dev_type != 0) && (dev_type != 0xdeadc0de) + && (rom_version == 0x99999999) && (build_version == 1)) { + path[0] = '\0'; + } else + sprintf(path, "%s%s", FW_PATH, PATCH_FILE); + + return 0; +} + +static int get_device_type(int dev, uint32_t *code) +{ + uint8_t hciCommand[] = { + 0x05, 0xfc, 0x05, 0x00, 0x00, 0x00, 0x00, 0x04 + }; + uint8_t *event; + uint8_t *buffer_to_free = NULL; + uint32_t reg; + + int result = -1; + *code = 0; + + hciCommand[3] = (uint8_t) (FPGA_REGISTER & 0xFF); + hciCommand[4] = (uint8_t) ((FPGA_REGISTER >> 8) & 0xFF); + hciCommand[5] = (uint8_t) ((FPGA_REGISTER >> 16) & 0xFF); + hciCommand[6] = (uint8_t) ((FPGA_REGISTER >> 24) & 0xFF); + + if (send_hci_cmd_wait_event(dev, hciCommand, + sizeof(hciCommand), &event, + &buffer_to_free) == 0) { + if (event[5] == 0xFC && event[6] == 0x00) { + + switch (event[4]) { + + case 0x05: + reg = event[10]; + reg = ((reg << 8) | event[9]); + reg = ((reg << 8) | event[8]); + reg = ((reg << 8) | event[7]); + *code = reg; + result = 0; + break; + + case 0x06: + break; + } + } + } + + if (buffer_to_free != NULL) + free(buffer_to_free); + + return result; +} + +static int read_ar3k_version(int pConfig) +{ + uint8_t hciCommand[] = { + 0x1E, 0xfc, 0x00 + }; + uint8_t *event; + uint8_t *buffer_to_free = NULL; + int result = -1; + + if (0 == + send_hci_cmd_wait_event(pConfig, hciCommand, + sizeof(hciCommand), &event, + &buffer_to_free)) { + result = read_ps_event(event); + } + if (buffer_to_free != NULL) + free(buffer_to_free); + + return result; +} + +static int str2bdaddr(char *str_bdaddr, char *bdaddr) +{ + char bdbyte[3]; + char *str_byte = str_bdaddr; + int i, j; + unsigned char colon_present = 0; + + if (strstr(str_bdaddr, ":") != NULL) + colon_present = 1; + + bdbyte[2] = '\0'; + + bdbyte[0] = str_byte[0]; + bdbyte[1] = str_byte[1]; + + for (i = 0, j = 5; i < 6; i++, j--) { + bdaddr[j] = strtol(bdbyte, NULL, 16); + + if (colon_present == 1) + str_byte += 3; + else + str_byte += 2; + } + return 0; +} + +static int write_bdaddr(int pConfig, char *bdaddr) +{ + uint8_t bdaddr_cmd[] = { 0x0B, 0xFC, 0x0A, 0x01, 0x01, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + uint8_t *event; + uint8_t *buffer_to_free = NULL; + int result = -1; + + str2bdaddr(bdaddr, (char *)&bdaddr_cmd[7]); + + if (0 == send_hci_cmd_wait_event(pConfig, bdaddr_cmd, + sizeof(bdaddr_cmd), + &event, &buffer_to_free)) { + + if (event[5] == 0xFC && event[6] == 0x00) { + if (event[4] == 0x0B) + result = 0; + } + + } else + perror(" Write failed \n"); + + if (buffer_to_free != NULL) + free(buffer_to_free); + + return result; +} + +int ath_ps_download(int hdev) +{ + int i; + int status; + struct ps_cmd_packet *hci_cmd_list; /* List storing the commands */ + uint32_t numCmds; + uint8_t *event; + uint8_t *buffer_to_free; + uint32_t DevType; + char patchFileName[PATH_MAX]; + char PsFileName[PATH_MAX]; + char bdaddr_file_name[PATH_MAX]; + FILE *stream; + char bdaddr[21]; + + status = 0; + hci_cmd_list = NULL; + + /* First verify if the controller is an FPGA or ASIC, + *so depending on the device type the PS file to be written + * will be different. + */ + do { + if (get_device_type(hdev, &DevType) == -1) { + status = -1; + break; + } + if (read_ar3k_version(hdev) == -1) { + status = -1; + break; + } + + get_ps_file_name(DevType, PsFileName); + get_patch_file_name(DevType, rom_version, build_version, + patchFileName); + + /* Read the PS file to a dynamically allocated buffer */ + stream = fopen(PsFileName, "r"); + if (stream == NULL) { + perror("firmware file open error\n"); + status = -1; + break; + } + status = ath_parse_ps(stream); + fclose(stream); + + /* + * It is not necessary that Patch file be available, + * continue with PS Operations if. + * failed. + */ + if (patchFileName[0] == '\0') + status = 0; + stream = fopen(patchFileName, "r"); + if (stream == NULL) + status = 0; + else { + /* parse and store the Patch file contents to + * a global variables + */ + status = parse_patch_file(stream); + fclose(stream); + } + + /* Create an HCI command list + * from the parsed PS and patch information */ + ath_create_cmd_list(&hci_cmd_list, &numCmds); + + /* + * First Send the CRC packet, + * We have to continue with the PS operations + * only if the CRC packet has been replied with + * a Command complete event with status Error. + */ + if (send_hci_cmd_wait_event + (hdev, hci_cmd_list[0].Hcipacket, hci_cmd_list[0].packetLen, + &event, &buffer_to_free) == 0) { + + if (read_ps_event(event) == 0) { + + /* Exit if the status is success */ + if (buffer_to_free != NULL) + free(buffer_to_free); + + status = 0; + break; + } + if (buffer_to_free != NULL) + free(buffer_to_free); + } else { + status = -1; + break; + } + for (i = 1; i < numCmds; i++) { + + if (send_hci_cmd_wait_event + (hdev, hci_cmd_list[i].Hcipacket, + hci_cmd_list[i].packetLen, &event, + &buffer_to_free) == 0) { + + if (read_ps_event(event) < 0) { + + /* Exit if the status is not success */ + if (buffer_to_free != NULL) + free(buffer_to_free); + + status = -1; + break; + } + if (buffer_to_free != NULL) + free(buffer_to_free); + } else { + status = 0; + break; + } + } + /* Read the PS file to a dynamically allocated buffer */ + sprintf(bdaddr_file_name, "%s%s", FW_PATH, BDADDR_FILE); + stream = fopen(bdaddr_file_name, "r"); + + if (stream == NULL) { + status = 0; + break; + } + + if (fgets(bdaddr, 20, stream) != NULL) + status = write_bdaddr(hdev, bdaddr); + + fclose(stream); + + } while (FALSE); + + if (hci_cmd_list != NULL) + ath_free_command_list(&hci_cmd_list, numCmds); + + return status; +} -- 1.7.0 ^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [PATCH] Added support for Atheros AR300x Bluetooth Chip 2010-03-15 5:01 [PATCH] Added support for Atheros AR300x Bluetooth Chip suraj 2010-03-24 5:27 ` suraj @ 2010-04-19 23:53 ` Gustavo F. Padovan 2010-04-20 10:20 ` [PATCH v3] " suraj 2 siblings, 0 replies; 36+ messages in thread From: Gustavo F. Padovan @ 2010-04-19 23:53 UTC (permalink / raw) To: suraj; +Cc: linux-bluetooth, marcel, Luis.Rodriguez, Jothikumar.Mothilal Hi Suraj, * suraj <suraj@atheros.com> [2010-03-15 10:31:36 +0530]: > > This protocol implements support for power management feature provided by AR300x chip. > This lets the controller chip go to sleep mode if there is no Bluetooth activity for some time. > It then wakes up the chip in case of a Bluetooth activity. > > > Signed-off-by: Suraj <suraj@atheros.com> > > --- > drivers/bluetooth/Kconfig | 11 ++ > drivers/bluetooth/Makefile | 1 + > drivers/bluetooth/hci_ath.c | 353 +++++++++++++++++++++++++++++++++++++++++ > drivers/bluetooth/hci_ldisc.c | 6 + > drivers/bluetooth/hci_uart.h | 8 +- > 5 files changed, 378 insertions(+), 1 deletions(-) > create mode 100755 drivers/bluetooth/hci_ath.c > > diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig > index 058fbcc..81abeff 100644 > --- a/drivers/bluetooth/Kconfig > +++ b/drivers/bluetooth/Kconfig > @@ -58,6 +58,17 @@ config BT_HCIUART_BCSP > > Say Y here to compile support for HCI BCSP protocol. > > +config BT_HCIUART_ATH > + bool "Atheros AR300x Board support" > + depends on BT_HCIUART > + help > + HCIATH (HCI Atheros) is a serial protocol for communication > + between Bluetooth device and host with support for Atheros AR300x > + power management feature. This protocol is required for > + serial Bluetooth devices that are based on Atheros AR300x chips. > + > + Say Y here to compile support for HCIATH protocol. > + > config BT_HCIUART_LL > bool "HCILL protocol support" > depends on BT_HCIUART > diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile > index 7e5aed5..1481faa 100644 > --- a/drivers/bluetooth/Makefile > +++ b/drivers/bluetooth/Makefile > @@ -26,4 +26,5 @@ hci_uart-y := hci_ldisc.o > hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o > hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o > hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o > +hci_uart-$(CONFIG_BT_HCIUART_ATH) += hci_ath.o > hci_uart-objs := $(hci_uart-y) > diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c > new file mode 100755 > index 0000000..13e4404 > --- /dev/null > +++ b/drivers/bluetooth/hci_ath.c > @@ -0,0 +1,353 @@ > +/* > + * Copyright (c) 2009-2010 Atheros Communications Inc. > + * > + * 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 <linux/module.h> > +#include <linux/kernel.h> > + > +#include <linux/init.h> > +#include <linux/slab.h> > +#include <linux/tty.h> > +#include <linux/errno.h> > +#include <linux/ioctl.h> > +#include <linux/skbuff.h> > + > +#include <net/bluetooth/bluetooth.h> > +#include <net/bluetooth/hci_core.h> > + > +#include "hci_uart.h" > + > + > +/* HCIATH receiver States */ > +#define HCIATH_W4_PACKET_TYPE 0 > +#define HCIATH_W4_EVENT_HDR 1 > +#define HCIATH_W4_ACL_HDR 2 > +#define HCIATH_W4_SCO_HDR 3 > +#define HCIATH_W4_DATA 4 > + > +struct ath_struct { > + struct hci_uart *hu; > + unsigned int rx_state; > + unsigned int rx_count; > + unsigned int cur_sleep; > + > + spinlock_t hciath_lock; > + struct sk_buff *rx_skb; > + struct sk_buff_head txq; > + wait_queue_head_t wqevt; > + struct work_struct ctxtsw; > +}; > + > +int ath_wakeup_ar3001(struct tty_struct *tty) > +{ > + struct termios settings; > + int status = 0x00; > + mm_segment_t oldfs; > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + if ((status & TIOCM_CTS)) > + return status; > + > + oldfs = get_fs(); > + set_fs(KERNEL_DS); > + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); > + > + settings.c_cflag &= ~CRTSCTS; > + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); > + set_fs(oldfs); > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + /* Wake up board */ > + tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS); > + mdelay(20); > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00); > + mdelay(20); > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + oldfs = get_fs(); > + set_fs(KERNEL_DS); > + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); > + > + settings.c_cflag |= CRTSCTS; > + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); > + set_fs(oldfs); > + return status; > +} > + > +static void ath_context_switch(struct work_struct *work) > +{ > + int status; > + struct ath_struct *ath; > + struct hci_uart *hu; > + struct tty_struct *tty; > + > + ath = container_of(work, struct ath_struct, ctxtsw); > + > + hu = ath->hu; > + tty = hu->tty; > + > + /* verify and wake up controller */ > + if (ath->cur_sleep) { > + Extra new line here. > + status = ath_wakeup_ar3001(tty); > + if (!(status & TIOCM_CTS)) > + return; > + } > + > + /* Ready to send Data */ > + clear_bit(HCI_UART_SENDING, &hu->tx_state); > + hci_uart_tx_wakeup(hu); > +} > + > +int ath_check_sleep_cmd(struct ath_struct *ath, unsigned char *packet) > +{ > + if (packet[0] == 0x04 && packet[1] == 0xFC) > + ath->cur_sleep = packet[3]; > + > + return 0; > +} > + > + > +/* Initialize protocol */ > +static int ath_open(struct hci_uart *hu) > +{ > + struct ath_struct *ath; > + BT_DBG("hu %p", hu); > + > + ath = kzalloc(sizeof(*ath), GFP_ATOMIC); > + if (!ath) > + return -ENOMEM; > + > + skb_queue_head_init(&ath->txq); > + spin_lock_init(&ath->hciath_lock); > + > + ath->cur_sleep = 0; You've used kzalloc, so cur_sleep is already 0 > + hu->priv = ath; > + ath->hu = hu; > + > + init_waitqueue_head(&ath->wqevt); > + INIT_WORK(&ath->ctxtsw, ath_context_switch); > + return 0; > +} > + > +/* Flush protocol data */ > +static int ath_flush(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + BT_DBG("hu %p", hu); > + skb_queue_purge(&ath->txq); > + > + return 0; > +} > + > +/* Close protocol */ > +static int ath_close(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; Add a extra line before the BT_DBG() > + BT_DBG("hu %p", hu); > + > + skb_queue_purge(&ath->txq); > + > + if (ath->rx_skb) > + kfree_skb(ath->rx_skb); > + > + wake_up_interruptible(&ath->wqevt); > + hu->priv = NULL; > + kfree(ath); > + return 0; > +} > + > +/* Enqueue frame for transmittion */ > +static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) > +{ > + struct ath_struct *ath = hu->priv; > + if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { > + > + /* Discard SCO packet.AR3001 does not support SCO over HCI */ > + BT_DBG("SCO Packet over HCI received Dropping\n"); > + kfree(skb); > + return 0; > + } > + BT_DBG("hu %p skb %p", hu, skb); Add a extra line before the BT_DBG() > + > + /* Prepend skb with frame type */ > + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); > + > + skb_queue_tail(&ath->txq, skb); > + set_bit(HCI_UART_SENDING, &hu->tx_state); > + > + schedule_work(&ath->ctxtsw); > + return 0; > +} > + > +static struct sk_buff *ath_dequeue(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + struct sk_buff *skbuf; > + > + skbuf = skb_dequeue(&ath->txq); > + if (skbuf != NULL) > + ath_check_sleep_cmd(ath, &skbuf->data[1]); > + > + return skbuf; > +} > + > +static inline int ath_check_data_len(struct ath_struct *ath, int len) > +{ > + register int room = skb_tailroom(ath->rx_skb); > + BT_DBG("len %d room %d", len, room); > + > + if (len > room) { > + BT_ERR("Data length is too large"); > + kfree_skb(ath->rx_skb); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_skb = NULL; > + ath->rx_count = 0; > + } else { > + ath->rx_state = HCIATH_W4_DATA; > + ath->rx_count = len; > + return len; > + } > + > + return 0; You can return ath->rx_count here and remove the return from the else. Or ad the return 0 on the if (len > room). > +} > + > +/* Recv data */ > +static int ath_recv(struct hci_uart *hu, void *data, int count) > +{ > + struct ath_struct *ath = hu->priv; > + register char *ptr; > + struct hci_event_hdr *eh; > + struct hci_acl_hdr *ah; > + struct hci_sco_hdr *sh; > + struct sk_buff *skbuf; > + register int len, type, dlen; Wouldn't be better let the compiler choose which vars it will put on the registers? > + > + skbuf = NULL; You never use skbuf after here. > + BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, > + ath->rx_state, ath->rx_count); > + ptr = data; You can set ptr to data when you declare it. > + while (count) { > + if (ath->rx_count) { > + > + len = min_t(unsigned int, ath->rx_count, count); > + memcpy(skb_put(ath->rx_skb, len), ptr, len); > + ath->rx_count -= len; > + count -= len; > + ptr += len; > + > + if (ath->rx_count) > + continue; > + switch (ath->rx_state) { > + case HCIATH_W4_DATA: > + hci_recv_frame(ath->rx_skb); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_skb = NULL; > + ath->rx_count = 0; > + continue; > + case HCIATH_W4_EVENT_HDR: > + eh = (struct hci_event_hdr *)ath->rx_skb->data; > + BT_DBG("Event header: evt 0x%2.2x plen %d", > + eh->evt, eh->plen); > + ath_check_data_len(ath, eh->plen); > + continue; > + case HCIATH_W4_ACL_HDR: > + ah = (struct hci_acl_hdr *)ath->rx_skb->data; > + dlen = __le16_to_cpu(ah->dlen); > + BT_DBG("ACL header: dlen %d", dlen); > + ath_check_data_len(ath, dlen); > + continue; > + case HCIATH_W4_SCO_HDR: > + sh = (struct hci_sco_hdr *)ath->rx_skb->data; > + BT_DBG("SCO header: dlen %d", sh->dlen); > + ath_check_data_len(ath, sh->dlen); > + continue; > + } > + } > + > + /* HCIATH_W4_PACKET_TYPE */ > + switch (*ptr) { > + case HCI_EVENT_PKT: > + BT_DBG("Event packet"); > + ath->rx_state = HCIATH_W4_EVENT_HDR; > + ath->rx_count = HCI_EVENT_HDR_SIZE; > + type = HCI_EVENT_PKT; > + break; > + case HCI_ACLDATA_PKT: > + BT_DBG("ACL packet"); > + ath->rx_state = HCIATH_W4_ACL_HDR; > + ath->rx_count = HCI_ACL_HDR_SIZE; > + type = HCI_ACLDATA_PKT; > + break; > + case HCI_SCODATA_PKT: > + BT_DBG("SCO packet"); > + ath->rx_state = HCIATH_W4_SCO_HDR; > + ath->rx_count = HCI_SCO_HDR_SIZE; > + type = HCI_SCODATA_PKT; > + break; > + default: > + BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); > + hu->hdev->stat.err_rx++; > + ptr++; > + count--; > + continue; > + }; > + ptr++; > + count--; > + > + /* Allocate packet */ > + ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); > + if (!ath->rx_skb) { > + BT_ERR("Can't allocate mem for new packet"); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_count = 0; > + return -ENOMEM; > + } > + ath->rx_skb->dev = (void *)hu->hdev; > + bt_cb(ath->rx_skb)->pkt_type = type; > + } return count; > +} > + > +static struct hci_uart_proto athp = { > + .id = HCI_UART_ATH, > + .open = ath_open, > + .close = ath_close, > + .recv = ath_recv, > + .enqueue = ath_enqueue, > + .dequeue = ath_dequeue, > + .flush = ath_flush, > +}; > + > +int ath_init(void) > +{ > + int err = hci_uart_register_proto(&athp); > + if (!err) > + BT_INFO("HCIATH protocol initialized"); > + > + else > + BT_ERR("HCIATH protocol registration failed"); Add a new line here. > + return err; > +} > + > +int ath_deinit(void) > +{ > + return hci_uart_unregister_proto(&athp); > +} > diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c > index 76a1abb..7dd76d1 100644 > --- a/drivers/bluetooth/hci_ldisc.c > +++ b/drivers/bluetooth/hci_ldisc.c > @@ -542,6 +542,9 @@ static int __init hci_uart_init(void) > #ifdef CONFIG_BT_HCIUART_LL > ll_init(); > #endif > +#ifdef CONFIG_BT_HCIUART_ATH > + ath_init(); > +#endif > > return 0; > } > @@ -559,6 +562,9 @@ static void __exit hci_uart_exit(void) > #ifdef CONFIG_BT_HCIUART_LL > ll_deinit(); > #endif > +#ifdef CONFIG_BT_HCIUART_ATH > + ath_deinit(); > +#endif > > /* Release tty registration of line discipline */ > if ((err = tty_unregister_ldisc(N_HCI))) > diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h > index 50113db..385537f 100644 > --- a/drivers/bluetooth/hci_uart.h > +++ b/drivers/bluetooth/hci_uart.h > @@ -33,13 +33,14 @@ > #define HCIUARTGETDEVICE _IOR('U', 202, int) > > /* UART protocols */ > -#define HCI_UART_MAX_PROTO 5 > +#define HCI_UART_MAX_PROTO 6 > > #define HCI_UART_H4 0 > #define HCI_UART_BCSP 1 > #define HCI_UART_3WIRE 2 > #define HCI_UART_H4DS 3 > #define HCI_UART_LL 4 > +#define HCI_UART_ATH 5 > > struct hci_uart; > > @@ -91,3 +92,8 @@ int bcsp_deinit(void); > int ll_init(void); > int ll_deinit(void); > #endif > + > +#ifdef CONFIG_BT_HCIUART_ATH > +int ath_init(void); > +int ath_deinit(void); > +#endif > -- > 1.6.3.3 > > > > -- > To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Gustavo F. Padovan http://padovan.org ^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v3] Added support for Atheros AR300x Bluetooth Chip 2010-03-15 5:01 [PATCH] Added support for Atheros AR300x Bluetooth Chip suraj 2010-03-24 5:27 ` suraj 2010-04-19 23:53 ` [PATCH] Added support for Atheros AR300x " Gustavo F. Padovan @ 2010-04-20 10:20 ` suraj 2010-04-20 15:36 ` Gustavo F. Padovan ` (2 more replies) 2 siblings, 3 replies; 36+ messages in thread From: suraj @ 2010-04-20 10:20 UTC (permalink / raw) To: linux-bluetooth; +Cc: marcel, Luis.Rodriguez, Jothikumar.Mothilal, gfpadovan This protocol implements support for power management feature provided by AR300x chip. This lets the controller chip go to sleep mode if there is no Bluetooth activity for some time. It then wakes up the chip in case of a Bluetooth activity. * Third version ** Updated with extra spacing and indentation ** made function definitions static ** Removed inline and register keyword usage. ** Removed unused return calls. ** Incorporated code comments by Luis and Gustavo Thanks Luis and Gustavo for your comments Signed-off-by: Suraj <suraj@atheros.com> --- drivers/bluetooth/Kconfig | 11 ++ drivers/bluetooth/Makefile | 1 + drivers/bluetooth/hci_ath.c | 384 +++++++++++++++++++++++++++++++++++++++++ drivers/bluetooth/hci_ldisc.c | 6 + drivers/bluetooth/hci_uart.h | 8 +- 5 files changed, 409 insertions(+), 1 deletions(-) create mode 100755 drivers/bluetooth/hci_ath.c diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 058fbcc..81abeff 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -58,6 +58,17 @@ config BT_HCIUART_BCSP Say Y here to compile support for HCI BCSP protocol. +config BT_HCIUART_ATH + bool "Atheros AR300x Board support" + depends on BT_HCIUART + help + HCIATH (HCI Atheros) is a serial protocol for communication + between Bluetooth device and host with support for Atheros AR300x + power management feature. This protocol is required for + serial Bluetooth devices that are based on Atheros AR300x chips. + + Say Y here to compile support for HCIATH protocol. + config BT_HCIUART_LL bool "HCILL protocol support" depends on BT_HCIUART diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 7e5aed5..1481faa 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -26,4 +26,5 @@ hci_uart-y := hci_ldisc.o hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o +hci_uart-$(CONFIG_BT_HCIUART_ATH) += hci_ath.o hci_uart-objs := $(hci_uart-y) diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c new file mode 100755 index 0000000..2f91954 --- /dev/null +++ b/drivers/bluetooth/hci_ath.c @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2009-2010 Atheros Communications Inc. + * + * 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 <linux/module.h> +#include <linux/kernel.h> + +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/errno.h> +#include <linux/ioctl.h> +#include <linux/skbuff.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +#include "hci_uart.h" + + +/* HCIATH receiver States */ +#define HCIATH_W4_PACKET_TYPE 0 +#define HCIATH_W4_EVENT_HDR 1 +#define HCIATH_W4_ACL_HDR 2 +#define HCIATH_W4_SCO_HDR 3 +#define HCIATH_W4_DATA 4 + +struct ath_struct { + struct hci_uart *hu; + unsigned int rx_state; + unsigned int rx_count; + unsigned int cur_sleep; + + spinlock_t hciath_lock; + struct sk_buff *rx_skb; + struct sk_buff_head txq; + wait_queue_head_t wqevt; + struct work_struct ctxtsw; +}; + +static int ath_wakeup_ar3001(struct tty_struct *tty) +{ + struct termios settings; + int status = 0x00; + + status = tty->driver->ops->tiocmget(tty, NULL); + + if ((status & TIOCM_CTS)) + return status; + + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); + + /* Disable Automatic RTSCTS */ + settings.c_cflag &= ~CRTSCTS; + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); + + status = tty->driver->ops->tiocmget(tty, NULL); + + /* Clear RTS first */ + tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS); + mdelay(20); + + status = tty->driver->ops->tiocmget(tty, NULL); + + /* Set RTS, wake up board */ + tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00); + mdelay(20); + + status = tty->driver->ops->tiocmget(tty, NULL); + + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); + + settings.c_cflag |= CRTSCTS; + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); + + return status; +} + +static void ath_context_switch(struct work_struct *work) +{ + int status; + struct ath_struct *ath; + struct hci_uart *hu; + struct tty_struct *tty; + + ath = container_of(work, struct ath_struct, ctxtsw); + + hu = ath->hu; + tty = hu->tty; + + /* verify and wake up controller */ + if (ath->cur_sleep) { + + status = ath_wakeup_ar3001(tty); + + if (!(status & TIOCM_CTS)) + return; + } + + /* Ready to send Data */ + clear_bit(HCI_UART_SENDING, &hu->tx_state); + hci_uart_tx_wakeup(hu); +} + +/* Initialize protocol */ +static int ath_open(struct hci_uart *hu) +{ + struct ath_struct *ath; + + BT_DBG("hu %p", hu); + + ath = kzalloc(sizeof(*ath), GFP_ATOMIC); + if (!ath) + return -ENOMEM; + + skb_queue_head_init(&ath->txq); + spin_lock_init(&ath->hciath_lock); + + hu->priv = ath; + ath->hu = hu; + + init_waitqueue_head(&ath->wqevt); + INIT_WORK(&ath->ctxtsw, ath_context_switch); + + return 0; +} + +/* Flush protocol data */ +static int ath_flush(struct hci_uart *hu) +{ + struct ath_struct *ath = hu->priv; + + BT_DBG("hu %p", hu); + + skb_queue_purge(&ath->txq); + + return 0; +} + +/* Close protocol */ +static int ath_close(struct hci_uart *hu) +{ + struct ath_struct *ath = hu->priv; + + BT_DBG("hu %p", hu); + + skb_queue_purge(&ath->txq); + + if (ath->rx_skb) + kfree_skb(ath->rx_skb); + + cancel_work_sync(&ath->ctxtsw); + + hu->priv = NULL; + kfree(ath); + + return 0; +} + +/* Enqueue frame for transmittion */ +static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) +{ + struct ath_struct *ath = hu->priv; + + if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { + + /* Discard SCO packet.AR3001 does not support SCO over HCI */ + BT_DBG("SCO Packet over HCI received Dropping"); + + kfree(skb); + + return 0; + } + + BT_DBG("hu %p skb %p", hu, skb); + + /* Prepend skb with frame type */ + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + + skb_queue_tail(&ath->txq, skb); + set_bit(HCI_UART_SENDING, &hu->tx_state); + + schedule_work(&ath->ctxtsw); + + return 0; +} + +static struct sk_buff *ath_dequeue(struct hci_uart *hu) +{ + struct ath_struct *ath = hu->priv; + struct sk_buff *skbuf; + + skbuf = skb_dequeue(&ath->txq); + + if (!skbuf) + return NULL; + + + /* + * Check if the HCI command is HCI sleep enable and + * update the sleep enable flag with command parameter. + * + * Value of sleep enable flag will be used later + * to verify if controller has to be woken up before + * sending any packet. + */ + if (skbuf->data[0] == 0x01 && skbuf->data[1] == 0x04 && + skbuf->data[2] == 0xFC) + ath->cur_sleep = skbuf->data[4]; + + return skbuf; +} + +static void ath_check_data_len(struct ath_struct *ath, int len) +{ + int room = skb_tailroom(ath->rx_skb); + + BT_DBG("len %d room %d", len, room); + + if (len > room) { + BT_ERR("Data length is too large"); + kfree_skb(ath->rx_skb); + ath->rx_state = HCIATH_W4_PACKET_TYPE; + ath->rx_skb = NULL; + ath->rx_count = 0; + } else { + ath->rx_state = HCIATH_W4_DATA; + ath->rx_count = len; + } +} + +/* Recv data */ +static int ath_recv(struct hci_uart *hu, void *data, int count) +{ + struct ath_struct *ath = hu->priv; + char *ptr = data; + struct hci_event_hdr *eh; + struct hci_acl_hdr *ah; + struct hci_sco_hdr *sh; + int len, type, dlen; + + + BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, + ath->rx_state, ath->rx_count); + + while (count) { + if (ath->rx_count) { + + len = min_t(unsigned int, ath->rx_count, count); + memcpy(skb_put(ath->rx_skb, len), ptr, len); + ath->rx_count -= len; + count -= len; + ptr += len; + + if (ath->rx_count) + continue; + switch (ath->rx_state) { + case HCIATH_W4_DATA: + hci_recv_frame(ath->rx_skb); + ath->rx_state = HCIATH_W4_PACKET_TYPE; + ath->rx_skb = NULL; + ath->rx_count = 0; + continue; + + case HCIATH_W4_EVENT_HDR: + eh = (struct hci_event_hdr *)ath->rx_skb->data; + + BT_DBG("Event header: evt 0x%2.2x plen %d", + eh->evt, eh->plen); + + ath_check_data_len(ath, eh->plen); + continue; + + case HCIATH_W4_ACL_HDR: + ah = (struct hci_acl_hdr *)ath->rx_skb->data; + dlen = __le16_to_cpu(ah->dlen); + + BT_DBG("ACL header: dlen %d", dlen); + + ath_check_data_len(ath, dlen); + continue; + + case HCIATH_W4_SCO_HDR: + sh = (struct hci_sco_hdr *)ath->rx_skb->data; + + BT_DBG("SCO header: dlen %d", sh->dlen); + + ath_check_data_len(ath, sh->dlen); + continue; + + } + } + + /* HCIATH_W4_PACKET_TYPE */ + switch (*ptr) { + case HCI_EVENT_PKT: + BT_DBG("Event packet"); + ath->rx_state = HCIATH_W4_EVENT_HDR; + ath->rx_count = HCI_EVENT_HDR_SIZE; + type = HCI_EVENT_PKT; + break; + + case HCI_ACLDATA_PKT: + BT_DBG("ACL packet"); + ath->rx_state = HCIATH_W4_ACL_HDR; + ath->rx_count = HCI_ACL_HDR_SIZE; + type = HCI_ACLDATA_PKT; + break; + + case HCI_SCODATA_PKT: + BT_DBG("SCO packet"); + ath->rx_state = HCIATH_W4_SCO_HDR; + ath->rx_count = HCI_SCO_HDR_SIZE; + type = HCI_SCODATA_PKT; + break; + + default: + BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); + hu->hdev->stat.err_rx++; + ptr++; + count--; + continue; + + }; + ptr++; + count--; + + /* Allocate packet */ + ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + if (!ath->rx_skb) { + BT_ERR("Can't allocate mem for new packet"); + ath->rx_state = HCIATH_W4_PACKET_TYPE; + ath->rx_count = 0; + + return -ENOMEM; + } + ath->rx_skb->dev = (void *)hu->hdev; + bt_cb(ath->rx_skb)->pkt_type = type; + } + + return count; +} + +static struct hci_uart_proto athp = { + .id = HCI_UART_ATH, + .open = ath_open, + .close = ath_close, + .recv = ath_recv, + .enqueue = ath_enqueue, + .dequeue = ath_dequeue, + .flush = ath_flush, +}; + +int ath_init(void) +{ + int err = hci_uart_register_proto(&athp); + + if (!err) + BT_INFO("HCIATH protocol initialized"); + else + BT_ERR("HCIATH protocol registration failed"); + + return err; +} + +int ath_deinit(void) +{ + return hci_uart_unregister_proto(&athp); +} diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 76a1abb..7dd76d1 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -542,6 +542,9 @@ static int __init hci_uart_init(void) #ifdef CONFIG_BT_HCIUART_LL ll_init(); #endif +#ifdef CONFIG_BT_HCIUART_ATH + ath_init(); +#endif return 0; } @@ -559,6 +562,9 @@ static void __exit hci_uart_exit(void) #ifdef CONFIG_BT_HCIUART_LL ll_deinit(); #endif +#ifdef CONFIG_BT_HCIUART_ATH + ath_deinit(); +#endif /* Release tty registration of line discipline */ if ((err = tty_unregister_ldisc(N_HCI))) diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 50113db..385537f 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -33,13 +33,14 @@ #define HCIUARTGETDEVICE _IOR('U', 202, int) /* UART protocols */ -#define HCI_UART_MAX_PROTO 5 +#define HCI_UART_MAX_PROTO 6 #define HCI_UART_H4 0 #define HCI_UART_BCSP 1 #define HCI_UART_3WIRE 2 #define HCI_UART_H4DS 3 #define HCI_UART_LL 4 +#define HCI_UART_ATH 5 struct hci_uart; @@ -91,3 +92,8 @@ int bcsp_deinit(void); int ll_init(void); int ll_deinit(void); #endif + +#ifdef CONFIG_BT_HCIUART_ATH +int ath_init(void); +int ath_deinit(void); +#endif -- 1.7.0 ^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [PATCH v3] Added support for Atheros AR300x Bluetooth Chip 2010-04-20 10:20 ` [PATCH v3] " suraj @ 2010-04-20 15:36 ` Gustavo F. Padovan 2010-04-20 17:34 ` Luis R. Rodriguez 2010-04-21 10:22 ` [PATCH v4] Add support for the " suraj 2 siblings, 0 replies; 36+ messages in thread From: Gustavo F. Padovan @ 2010-04-20 15:36 UTC (permalink / raw) To: suraj; +Cc: linux-bluetooth, marcel, Luis.Rodriguez, Jothikumar.Mothilal Hi Suraj, * suraj <suraj@atheros.com> [2010-04-20 15:50:32 +0530]: > This protocol implements support for power management feature provided by AR300x chip. > This lets the controller chip go to sleep mode if there is no Bluetooth > activity for some time. > It then wakes up the chip in case of a Bluetooth activity. > > * Third version > > ** Updated with extra spacing and indentation > ** made function definitions static > ** Removed inline and register keyword usage. > ** Removed unused return calls. > ** Incorporated code comments by Luis and Gustavo > > Thanks Luis and Gustavo for your comments > > > Signed-off-by: Suraj <suraj@atheros.com> > > --- > drivers/bluetooth/Kconfig | 11 ++ > drivers/bluetooth/Makefile | 1 + > drivers/bluetooth/hci_ath.c | 384 +++++++++++++++++++++++++++++++++++++++++ > drivers/bluetooth/hci_ldisc.c | 6 + > drivers/bluetooth/hci_uart.h | 8 +- > 5 files changed, 409 insertions(+), 1 deletions(-) > create mode 100755 drivers/bluetooth/hci_ath.c > > diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig > index 058fbcc..81abeff 100644 > --- a/drivers/bluetooth/Kconfig > +++ b/drivers/bluetooth/Kconfig > @@ -58,6 +58,17 @@ config BT_HCIUART_BCSP > > Say Y here to compile support for HCI BCSP protocol. > > +config BT_HCIUART_ATH > + bool "Atheros AR300x Board support" > + depends on BT_HCIUART > + help > + HCIATH (HCI Atheros) is a serial protocol for communication > + between Bluetooth device and host with support for Atheros AR300x > + power management feature. This protocol is required for > + serial Bluetooth devices that are based on Atheros AR300x chips. > + > + Say Y here to compile support for HCIATH protocol. > + > config BT_HCIUART_LL > bool "HCILL protocol support" > depends on BT_HCIUART > diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile > index 7e5aed5..1481faa 100644 > --- a/drivers/bluetooth/Makefile > +++ b/drivers/bluetooth/Makefile > @@ -26,4 +26,5 @@ hci_uart-y := hci_ldisc.o > hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o > hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o > hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o > +hci_uart-$(CONFIG_BT_HCIUART_ATH) += hci_ath.o > hci_uart-objs := $(hci_uart-y) > diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c > new file mode 100755 > index 0000000..2f91954 > --- /dev/null > +++ b/drivers/bluetooth/hci_ath.c > @@ -0,0 +1,384 @@ > +/* > + * Copyright (c) 2009-2010 Atheros Communications Inc. > + * > + * 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 <linux/module.h> > +#include <linux/kernel.h> > + > +#include <linux/init.h> > +#include <linux/slab.h> > +#include <linux/tty.h> > +#include <linux/errno.h> > +#include <linux/ioctl.h> > +#include <linux/skbuff.h> > + > +#include <net/bluetooth/bluetooth.h> > +#include <net/bluetooth/hci_core.h> > + > +#include "hci_uart.h" > + > + > +/* HCIATH receiver States */ > +#define HCIATH_W4_PACKET_TYPE 0 > +#define HCIATH_W4_EVENT_HDR 1 > +#define HCIATH_W4_ACL_HDR 2 > +#define HCIATH_W4_SCO_HDR 3 > +#define HCIATH_W4_DATA 4 > + > +struct ath_struct { > + struct hci_uart *hu; > + unsigned int rx_state; > + unsigned int rx_count; > + unsigned int cur_sleep; > + > + spinlock_t hciath_lock; > + struct sk_buff *rx_skb; > + struct sk_buff_head txq; > + wait_queue_head_t wqevt; > + struct work_struct ctxtsw; > +}; > + > +static int ath_wakeup_ar3001(struct tty_struct *tty) > +{ > + struct termios settings; > + int status = 0x00; > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + if ((status & TIOCM_CTS)) > + return status; > + > + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); > + > + /* Disable Automatic RTSCTS */ > + settings.c_cflag &= ~CRTSCTS; > + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + /* Clear RTS first */ > + tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS); > + mdelay(20); > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + /* Set RTS, wake up board */ > + tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00); > + mdelay(20); > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); > + > + settings.c_cflag |= CRTSCTS; > + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); > + > + return status; > +} > + > +static void ath_context_switch(struct work_struct *work) > +{ > + int status; > + struct ath_struct *ath; > + struct hci_uart *hu; > + struct tty_struct *tty; > + > + ath = container_of(work, struct ath_struct, ctxtsw); > + > + hu = ath->hu; > + tty = hu->tty; > + > + /* verify and wake up controller */ > + if (ath->cur_sleep) { > + Extra blank line here. > + status = ath_wakeup_ar3001(tty); > + > + if (!(status & TIOCM_CTS)) > + return; > + } > + > + /* Ready to send Data */ > + clear_bit(HCI_UART_SENDING, &hu->tx_state); > + hci_uart_tx_wakeup(hu); > +} > + > +/* Initialize protocol */ > +static int ath_open(struct hci_uart *hu) > +{ > + struct ath_struct *ath; > + > + BT_DBG("hu %p", hu); > + > + ath = kzalloc(sizeof(*ath), GFP_ATOMIC); > + if (!ath) > + return -ENOMEM; > + > + skb_queue_head_init(&ath->txq); > + spin_lock_init(&ath->hciath_lock); > + > + hu->priv = ath; > + ath->hu = hu; > + > + init_waitqueue_head(&ath->wqevt); > + INIT_WORK(&ath->ctxtsw, ath_context_switch); > + > + return 0; > +} > + > +/* Flush protocol data */ > +static int ath_flush(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + > + BT_DBG("hu %p", hu); > + > + skb_queue_purge(&ath->txq); > + > + return 0; > +} > + > +/* Close protocol */ > +static int ath_close(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + > + BT_DBG("hu %p", hu); > + > + skb_queue_purge(&ath->txq); > + > + if (ath->rx_skb) > + kfree_skb(ath->rx_skb); > + > + cancel_work_sync(&ath->ctxtsw); > + > + hu->priv = NULL; > + kfree(ath); > + > + return 0; > +} > + > +/* Enqueue frame for transmittion */ > +static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) > +{ > + struct ath_struct *ath = hu->priv; > + > + if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { > + > + /* Discard SCO packet.AR3001 does not support SCO over HCI */ > + BT_DBG("SCO Packet over HCI received Dropping"); > + > + kfree(skb); > + > + return 0; > + } > + > + BT_DBG("hu %p skb %p", hu, skb); > + > + /* Prepend skb with frame type */ > + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); > + > + skb_queue_tail(&ath->txq, skb); > + set_bit(HCI_UART_SENDING, &hu->tx_state); > + > + schedule_work(&ath->ctxtsw); > + > + return 0; > +} > + > +static struct sk_buff *ath_dequeue(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + struct sk_buff *skbuf; > + > + skbuf = skb_dequeue(&ath->txq); > + > + if (!skbuf) > + return NULL; > + > + Another extra blank line here. > + /* > + * Check if the HCI command is HCI sleep enable and > + * update the sleep enable flag with command parameter. > + * > + * Value of sleep enable flag will be used later > + * to verify if controller has to be woken up before > + * sending any packet. > + */ > + if (skbuf->data[0] == 0x01 && skbuf->data[1] == 0x04 && > + skbuf->data[2] == 0xFC) > + ath->cur_sleep = skbuf->data[4]; > + > + return skbuf; > +} > + > +static void ath_check_data_len(struct ath_struct *ath, int len) > +{ > + int room = skb_tailroom(ath->rx_skb); > + > + BT_DBG("len %d room %d", len, room); > + > + if (len > room) { > + BT_ERR("Data length is too large"); > + kfree_skb(ath->rx_skb); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_skb = NULL; > + ath->rx_count = 0; > + } else { > + ath->rx_state = HCIATH_W4_DATA; > + ath->rx_count = len; > + } > +} > + > +/* Recv data */ > +static int ath_recv(struct hci_uart *hu, void *data, int count) > +{ > + struct ath_struct *ath = hu->priv; > + char *ptr = data; > + struct hci_event_hdr *eh; > + struct hci_acl_hdr *ah; > + struct hci_sco_hdr *sh; > + int len, type, dlen; > + > + Extra blank line here too. > + BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, > + ath->rx_state, ath->rx_count); > + > + while (count) { > + if (ath->rx_count) { > + > + len = min_t(unsigned int, ath->rx_count, count); > + memcpy(skb_put(ath->rx_skb, len), ptr, len); > + ath->rx_count -= len; > + count -= len; > + ptr += len; > + > + if (ath->rx_count) > + continue; > + switch (ath->rx_state) { > + case HCIATH_W4_DATA: > + hci_recv_frame(ath->rx_skb); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_skb = NULL; > + ath->rx_count = 0; > + continue; > + > + case HCIATH_W4_EVENT_HDR: > + eh = (struct hci_event_hdr *)ath->rx_skb->data; > + > + BT_DBG("Event header: evt 0x%2.2x plen %d", > + eh->evt, eh->plen); > + > + ath_check_data_len(ath, eh->plen); > + continue; > + > + case HCIATH_W4_ACL_HDR: > + ah = (struct hci_acl_hdr *)ath->rx_skb->data; > + dlen = __le16_to_cpu(ah->dlen); > + > + BT_DBG("ACL header: dlen %d", dlen); > + > + ath_check_data_len(ath, dlen); > + continue; > + > + case HCIATH_W4_SCO_HDR: > + sh = (struct hci_sco_hdr *)ath->rx_skb->data; > + > + BT_DBG("SCO header: dlen %d", sh->dlen); > + > + ath_check_data_len(ath, sh->dlen); > + continue; > + > + } > + } > + > + /* HCIATH_W4_PACKET_TYPE */ > + switch (*ptr) { > + case HCI_EVENT_PKT: > + BT_DBG("Event packet"); > + ath->rx_state = HCIATH_W4_EVENT_HDR; > + ath->rx_count = HCI_EVENT_HDR_SIZE; > + type = HCI_EVENT_PKT; > + break; > + > + case HCI_ACLDATA_PKT: > + BT_DBG("ACL packet"); > + ath->rx_state = HCIATH_W4_ACL_HDR; > + ath->rx_count = HCI_ACL_HDR_SIZE; > + type = HCI_ACLDATA_PKT; > + break; > + > + case HCI_SCODATA_PKT: > + BT_DBG("SCO packet"); > + ath->rx_state = HCIATH_W4_SCO_HDR; > + ath->rx_count = HCI_SCO_HDR_SIZE; > + type = HCI_SCODATA_PKT; > + break; > + > + default: > + BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); > + hu->hdev->stat.err_rx++; > + ptr++; > + count--; > + continue; > + > + }; > + ptr++; > + count--; > + > + /* Allocate packet */ > + ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); > + if (!ath->rx_skb) { > + BT_ERR("Can't allocate mem for new packet"); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_count = 0; > + > + return -ENOMEM; > + } > + ath->rx_skb->dev = (void *)hu->hdev; > + bt_cb(ath->rx_skb)->pkt_type = type; > + } > + > + return count; > +} > + > +static struct hci_uart_proto athp = { > + .id = HCI_UART_ATH, > + .open = ath_open, > + .close = ath_close, > + .recv = ath_recv, > + .enqueue = ath_enqueue, > + .dequeue = ath_dequeue, > + .flush = ath_flush, > +}; > + > +int ath_init(void) > +{ > + int err = hci_uart_register_proto(&athp); > + > + if (!err) > + BT_INFO("HCIATH protocol initialized"); > + else > + BT_ERR("HCIATH protocol registration failed"); > + > + return err; > +} > + > +int ath_deinit(void) > +{ > + return hci_uart_unregister_proto(&athp); > +} > diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c > index 76a1abb..7dd76d1 100644 > --- a/drivers/bluetooth/hci_ldisc.c > +++ b/drivers/bluetooth/hci_ldisc.c > @@ -542,6 +542,9 @@ static int __init hci_uart_init(void) > #ifdef CONFIG_BT_HCIUART_LL > ll_init(); > #endif > +#ifdef CONFIG_BT_HCIUART_ATH > + ath_init(); > +#endif > > return 0; > } > @@ -559,6 +562,9 @@ static void __exit hci_uart_exit(void) > #ifdef CONFIG_BT_HCIUART_LL > ll_deinit(); > #endif > +#ifdef CONFIG_BT_HCIUART_ATH > + ath_deinit(); > +#endif > > /* Release tty registration of line discipline */ > if ((err = tty_unregister_ldisc(N_HCI))) > diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h > index 50113db..385537f 100644 > --- a/drivers/bluetooth/hci_uart.h > +++ b/drivers/bluetooth/hci_uart.h > @@ -33,13 +33,14 @@ > #define HCIUARTGETDEVICE _IOR('U', 202, int) > > /* UART protocols */ > -#define HCI_UART_MAX_PROTO 5 > +#define HCI_UART_MAX_PROTO 6 > > #define HCI_UART_H4 0 > #define HCI_UART_BCSP 1 > #define HCI_UART_3WIRE 2 > #define HCI_UART_H4DS 3 > #define HCI_UART_LL 4 > +#define HCI_UART_ATH 5 > > struct hci_uart; > > @@ -91,3 +92,8 @@ int bcsp_deinit(void); > int ll_init(void); > int ll_deinit(void); > #endif > + > +#ifdef CONFIG_BT_HCIUART_ATH > +int ath_init(void); > +int ath_deinit(void); > +#endif > -- > 1.7.0 > > > -- Gustavo F. Padovan http://padovan.org ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v3] Added support for Atheros AR300x Bluetooth Chip 2010-04-20 10:20 ` [PATCH v3] " suraj 2010-04-20 15:36 ` Gustavo F. Padovan @ 2010-04-20 17:34 ` Luis R. Rodriguez 2010-04-21 4:21 ` Suraj Sumangala 2010-04-21 10:22 ` [PATCH v4] Add support for the " suraj 2 siblings, 1 reply; 36+ messages in thread From: Luis R. Rodriguez @ 2010-04-20 17:34 UTC (permalink / raw) To: Suraj Sumangala Cc: linux-bluetooth@vger.kernel.org, marcel@holtmann.org, Luis Rodriguez, Jothikumar Mothilal, gfpadovan@gmail.com On Tue, Apr 20, 2010 at 03:20:32AM -0700, Suraj Sumangala wrote: > This protocol implements support for power management feature provided by AR300x chip. > This lets the controller chip go to sleep mode if there is no Bluetooth > activity for some time. > It then wakes up the chip in case of a Bluetooth activity. The above commit log needs some more work. Try to keep the commit log entry subject to about 50 characters, the context should not pass around 75 characters. Your commit also indicates this is a "protocol" ? This is driver, and you do have some hacks for enhancing power saving, but that is not that relevant to the commit log. How about: --- Add support for the Atheros AR300x Bluetooth Chip This adds support for the Atheros Bluetooth serial protocol to support the AR300x chipsets. The serial protocol implements enhanced power management features for the AR300x chipsets. Reviewed-by: Luis R. Rodriguez <lrodriguez@atheros.com> Signed-off-by: Suraj <suraj@atheros.com> --- Of course you will need to adjust the subject and prepend it with PATCH v4, just as you did with a v3 for this patch. Then, this stuff: > * Third version > > ** Updated with extra spacing and indentation Do you use checkpatch.pl for your patches? If not please add check your patches after committing them with: git show ./scripts/checkpatch.pl - vi drivers/bluetooth/hci_ath.c git commit -a --amend And repeat until checkpatch.pl is git happy :) > ** made function definitions static > ** Removed inline and register keyword usage. > ** Removed unused return calls. > ** Incorporated code comments by Luis and Gustavo > > Thanks Luis and Gustavo for your comments Remove it from the commit log, if you do want to add some extra text to the patch youc an put it below the three dashes ("-") where the diff stat goes: > > > Signed-off-by: Suraj <suraj@atheros.com> > > --- ^^^ These are the three lines, anything below is ignored by git am when the maintainer applies the patch. So you can add anything you want ignored by git am here. > drivers/bluetooth/Kconfig | 11 ++ > drivers/bluetooth/Makefile | 1 + > drivers/bluetooth/hci_ath.c | 384 +++++++++++++++++++++++++++++++++++++++++ > drivers/bluetooth/hci_ldisc.c | 6 + > drivers/bluetooth/hci_uart.h | 8 +- > 5 files changed, 409 insertions(+), 1 deletions(-) > create mode 100755 drivers/bluetooth/hci_ath.c > > diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig > index 058fbcc..81abeff 100644 > --- a/drivers/bluetooth/Kconfig > +++ b/drivers/bluetooth/Kconfig > @@ -58,6 +58,17 @@ config BT_HCIUART_BCSP > > Say Y here to compile support for HCI BCSP protocol. > > +config BT_HCIUART_ATH > + bool "Atheros AR300x Board support" > + depends on BT_HCIUART > + help > + HCIATH (HCI Atheros) is a serial protocol for communication > + between Bluetooth device and host with support for Atheros AR300x > + power management feature. This protocol is required for > + serial Bluetooth devices that are based on Atheros AR300x chips. Please adjust the description as well. How about: + HCIATH (HCI Atheros) is a serial protocol for communication + between the host and Atheros AR300x Bluetooth devices. The + protocol implements enhaned power management features for the + the AR300x chipsets, it lets the controller chip go to sleep + mode if there is no Bluetooth activity for some time and wakes + up the chip in case of a Bluetooth activity. Enabling this + option will build HCI Atheros support into the hci_uart driver. + Enable this option if you have an UART Atheros AR300x serial + device. > + > + Say Y here to compile support for HCIATH protocol. > + > config BT_HCIUART_LL > bool "HCILL protocol support" > depends on BT_HCIUART > diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile > index 7e5aed5..1481faa 100644 > --- a/drivers/bluetooth/Makefile > +++ b/drivers/bluetooth/Makefile > @@ -26,4 +26,5 @@ hci_uart-y := hci_ldisc.o > hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o > hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o > hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o > +hci_uart-$(CONFIG_BT_HCIUART_ATH) += hci_ath.o > hci_uart-objs := $(hci_uart-y) > diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c > new file mode 100755 > index 0000000..2f91954 > --- /dev/null > +++ b/drivers/bluetooth/hci_ath.c > @@ -0,0 +1,384 @@ > +/* > + * Copyright (c) 2009-2010 Atheros Communications Inc. > + * > + * 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 <linux/module.h> > +#include <linux/kernel.h> > + > +#include <linux/init.h> > +#include <linux/slab.h> > +#include <linux/tty.h> > +#include <linux/errno.h> > +#include <linux/ioctl.h> > +#include <linux/skbuff.h> > + > +#include <net/bluetooth/bluetooth.h> > +#include <net/bluetooth/hci_core.h> > + > +#include "hci_uart.h" > + > + > +/* HCIATH receiver States */ > +#define HCIATH_W4_PACKET_TYPE 0 > +#define HCIATH_W4_EVENT_HDR 1 > +#define HCIATH_W4_ACL_HDR 2 > +#define HCIATH_W4_SCO_HDR 3 > +#define HCIATH_W4_DATA 4 > + > +struct ath_struct { > + struct hci_uart *hu; > + unsigned int rx_state; > + unsigned int rx_count; > + unsigned int cur_sleep; > + > + spinlock_t hciath_lock; > + struct sk_buff *rx_skb; > + struct sk_buff_head txq; > + wait_queue_head_t wqevt; > + struct work_struct ctxtsw; > +}; > + > +static int ath_wakeup_ar3001(struct tty_struct *tty) > +{ > + struct termios settings; > + int status = 0x00; > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + if ((status & TIOCM_CTS)) > + return status; No need for double () parens here. This should be fine: + if (status & TIOCM_CTS) + return status; You would use double parens if you are doing a check against another flag as well. > + > + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); > + > + /* Disable Automatic RTSCTS */ > + settings.c_cflag &= ~CRTSCTS; > + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + /* Clear RTS first */ > + tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS); > + mdelay(20); > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + /* Set RTS, wake up board */ > + tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00); > + mdelay(20); > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); > + > + settings.c_cflag |= CRTSCTS; > + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); > + > + return status; > +} > + > +static void ath_context_switch(struct work_struct *work) Can you please rename this to ath_hci_uart_work() > +{ > + int status; > + struct ath_struct *ath; > + struct hci_uart *hu; > + struct tty_struct *tty; > + > + ath = container_of(work, struct ath_struct, ctxtsw); > + > + hu = ath->hu; > + tty = hu->tty; > + > + /* verify and wake up controller */ > + if (ath->cur_sleep) { > + > + status = ath_wakeup_ar3001(tty); > + > + if (!(status & TIOCM_CTS)) > + return; > + } > + > + /* Ready to send Data */ > + clear_bit(HCI_UART_SENDING, &hu->tx_state); > + hci_uart_tx_wakeup(hu); > +} > + > +/* Initialize protocol */ > +static int ath_open(struct hci_uart *hu) > +{ > + struct ath_struct *ath; > + > + BT_DBG("hu %p", hu); > + > + ath = kzalloc(sizeof(*ath), GFP_ATOMIC); > + if (!ath) > + return -ENOMEM; > + > + skb_queue_head_init(&ath->txq); > + spin_lock_init(&ath->hciath_lock); > + > + hu->priv = ath; > + ath->hu = hu; > + > + init_waitqueue_head(&ath->wqevt); > + INIT_WORK(&ath->ctxtsw, ath_context_switch); > + > + return 0; > +} > + > +/* Flush protocol data */ > +static int ath_flush(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + > + BT_DBG("hu %p", hu); > + > + skb_queue_purge(&ath->txq); > + > + return 0; > +} > + > +/* Close protocol */ > +static int ath_close(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + > + BT_DBG("hu %p", hu); > + > + skb_queue_purge(&ath->txq); > + > + if (ath->rx_skb) > + kfree_skb(ath->rx_skb); No need for the check, kfree_skb() does that for you and it has it optimized for the case where the skb you pass is NULL. You can just do: + kfree_skb(ath->rx_skb); > + > + cancel_work_sync(&ath->ctxtsw); > + > + hu->priv = NULL; > + kfree(ath); > + > + return 0; > +} Did you give this new patch a spin by looping bringing up, scanning, bringing the interface down? Or have a loop doing a scan while in another window you bring the interface up and down? > + > +/* Enqueue frame for transmittion */ > +static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) > +{ > + struct ath_struct *ath = hu->priv; > + > + if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { > + > + /* Discard SCO packet.AR3001 does not support SCO over HCI */ Add a space after packet. Why does it not support SCO over HCI BTW? Just curious. I'm new to BT :) Is this common? If this is common can't the BT stack be informed of these things so that they don't pass the skbs to the driver? This could be done in a separate patch though if this is the case though. > + BT_DBG("SCO Packet over HCI received Dropping"); > + > + kfree(skb); > + > + return 0; > + } > + > + BT_DBG("hu %p skb %p", hu, skb); > + > + /* Prepend skb with frame type */ > + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); > + > + skb_queue_tail(&ath->txq, skb); > + set_bit(HCI_UART_SENDING, &hu->tx_state); > + > + schedule_work(&ath->ctxtsw); > + > + return 0; > +} > + > +static struct sk_buff *ath_dequeue(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + struct sk_buff *skbuf; > + > + skbuf = skb_dequeue(&ath->txq); > + > + if (!skbuf) > + return NULL; > + > + > + /* > + * Check if the HCI command is HCI sleep enable and > + * update the sleep enable flag with command parameter. > + * > + * Value of sleep enable flag will be used later > + * to verify if controller has to be woken up before > + * sending any packet. > + */ > + if (skbuf->data[0] == 0x01 && skbuf->data[1] == 0x04 && > + skbuf->data[2] == 0xFC) > + ath->cur_sleep = skbuf->data[4]; Might as well just do this: + if (skbuf->data[0] == 0x01 && + skbuf->data[1] == 0x04 && + skbuf->data[2] == 0xFC) + ath->cur_sleep = skbuf->data[4]; Is this sort of check done in any other drivers/protocols? If so a helper could be added to hci_uart.h if this is the case, but likely better though a separate patch. > + > + return skbuf; > +} > + > +static void ath_check_data_len(struct ath_struct *ath, int len) > +{ > + int room = skb_tailroom(ath->rx_skb); > + > + BT_DBG("len %d room %d", len, room); > + > + if (len > room) { > + BT_ERR("Data length is too large"); > + kfree_skb(ath->rx_skb); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_skb = NULL; > + ath->rx_count = 0; > + } else { > + ath->rx_state = HCIATH_W4_DATA; > + ath->rx_count = len; > + } > +} > + > +/* Recv data */ > +static int ath_recv(struct hci_uart *hu, void *data, int count) > +{ > + struct ath_struct *ath = hu->priv; > + char *ptr = data; > + struct hci_event_hdr *eh; > + struct hci_acl_hdr *ah; > + struct hci_sco_hdr *sh; > + int len, type, dlen; > + > + > + BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, > + ath->rx_state, ath->rx_count); > + > + while (count) { > + if (ath->rx_count) { > + > + len = min_t(unsigned int, ath->rx_count, count); > + memcpy(skb_put(ath->rx_skb, len), ptr, len); > + ath->rx_count -= len; > + count -= len; > + ptr += len; > + > + if (ath->rx_count) > + continue; > + switch (ath->rx_state) { > + case HCIATH_W4_DATA: > + hci_recv_frame(ath->rx_skb); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_skb = NULL; > + ath->rx_count = 0; > + continue; > + > + case HCIATH_W4_EVENT_HDR: > + eh = (struct hci_event_hdr *)ath->rx_skb->data; > + > + BT_DBG("Event header: evt 0x%2.2x plen %d", > + eh->evt, eh->plen); > + > + ath_check_data_len(ath, eh->plen); > + continue; > + > + case HCIATH_W4_ACL_HDR: > + ah = (struct hci_acl_hdr *)ath->rx_skb->data; > + dlen = __le16_to_cpu(ah->dlen); > + > + BT_DBG("ACL header: dlen %d", dlen); > + > + ath_check_data_len(ath, dlen); > + continue; > + > + case HCIATH_W4_SCO_HDR: > + sh = (struct hci_sco_hdr *)ath->rx_skb->data; > + > + BT_DBG("SCO header: dlen %d", sh->dlen); > + > + ath_check_data_len(ath, sh->dlen); > + continue; > + > + } > + } > + > + /* HCIATH_W4_PACKET_TYPE */ > + switch (*ptr) { > + case HCI_EVENT_PKT: > + BT_DBG("Event packet"); > + ath->rx_state = HCIATH_W4_EVENT_HDR; > + ath->rx_count = HCI_EVENT_HDR_SIZE; > + type = HCI_EVENT_PKT; > + break; > + > + case HCI_ACLDATA_PKT: > + BT_DBG("ACL packet"); > + ath->rx_state = HCIATH_W4_ACL_HDR; > + ath->rx_count = HCI_ACL_HDR_SIZE; > + type = HCI_ACLDATA_PKT; > + break; > + > + case HCI_SCODATA_PKT: > + BT_DBG("SCO packet"); > + ath->rx_state = HCIATH_W4_SCO_HDR; > + ath->rx_count = HCI_SCO_HDR_SIZE; > + type = HCI_SCODATA_PKT; > + break; > + > + default: > + BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); > + hu->hdev->stat.err_rx++; > + ptr++; > + count--; > + continue; > + > + }; > + ptr++; > + count--; > + > + /* Allocate packet */ > + ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); > + if (!ath->rx_skb) { > + BT_ERR("Can't allocate mem for new packet"); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_count = 0; > + > + return -ENOMEM; > + } > + ath->rx_skb->dev = (void *)hu->hdev; > + bt_cb(ath->rx_skb)->pkt_type = type; > + } > + > + return count; > +} > + > +static struct hci_uart_proto athp = { > + .id = HCI_UART_ATH, > + .open = ath_open, > + .close = ath_close, > + .recv = ath_recv, > + .enqueue = ath_enqueue, > + .dequeue = ath_dequeue, > + .flush = ath_flush, > +}; > + > +int ath_init(void) > +{ > + int err = hci_uart_register_proto(&athp); > + > + if (!err) > + BT_INFO("HCIATH protocol initialized"); > + else > + BT_ERR("HCIATH protocol registration failed"); > + > + return err; > +} > + > +int ath_deinit(void) > +{ > + return hci_uart_unregister_proto(&athp); > +} > diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c > index 76a1abb..7dd76d1 100644 > --- a/drivers/bluetooth/hci_ldisc.c > +++ b/drivers/bluetooth/hci_ldisc.c > @@ -542,6 +542,9 @@ static int __init hci_uart_init(void) > #ifdef CONFIG_BT_HCIUART_LL > ll_init(); > #endif > +#ifdef CONFIG_BT_HCIUART_ATH > + ath_init(); > +#endif > > return 0; > } > @@ -559,6 +562,9 @@ static void __exit hci_uart_exit(void) > #ifdef CONFIG_BT_HCIUART_LL > ll_deinit(); > #endif > +#ifdef CONFIG_BT_HCIUART_ATH > + ath_deinit(); > +#endif > > /* Release tty registration of line discipline */ > if ((err = tty_unregister_ldisc(N_HCI))) > diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h > index 50113db..385537f 100644 > --- a/drivers/bluetooth/hci_uart.h > +++ b/drivers/bluetooth/hci_uart.h > @@ -33,13 +33,14 @@ > #define HCIUARTGETDEVICE _IOR('U', 202, int) > > /* UART protocols */ > -#define HCI_UART_MAX_PROTO 5 > +#define HCI_UART_MAX_PROTO 6 > > #define HCI_UART_H4 0 > #define HCI_UART_BCSP 1 > #define HCI_UART_3WIRE 2 > #define HCI_UART_H4DS 3 > #define HCI_UART_LL 4 > +#define HCI_UART_ATH 5 > > struct hci_uart; > > @@ -91,3 +92,8 @@ int bcsp_deinit(void); > int ll_init(void); > int ll_deinit(void); > #endif > + > +#ifdef CONFIG_BT_HCIUART_ATH > +int ath_init(void); > +int ath_deinit(void); > +#endif > -- > 1.7.0 > > > ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v3] Added support for Atheros AR300x Bluetooth Chip 2010-04-20 17:34 ` Luis R. Rodriguez @ 2010-04-21 4:21 ` Suraj Sumangala 0 siblings, 0 replies; 36+ messages in thread From: Suraj Sumangala @ 2010-04-21 4:21 UTC (permalink / raw) To: Luis Rodriguez Cc: Suraj Sumangala, linux-bluetooth@vger.kernel.org, marcel@holtmann.org, Jothikumar Mothilal, gfpadovan@gmail.com Hi Luis, Luis Rodriguez wrote: > On Tue, Apr 20, 2010 at 03:20:32AM -0700, Suraj Sumangala wrote: >> This protocol implements support for power management feature provided by AR300x chip. >> This lets the controller chip go to sleep mode if there is no Bluetooth >> activity for some time. >> It then wakes up the chip in case of a Bluetooth activity. > > The above commit log needs some more work. Try to keep the > commit log entry subject to about 50 characters, the context should > not pass around 75 characters. Your commit also indicates this is a > "protocol" ? This is driver, and you do have some hacks for enhancing > power saving, but that is not that relevant to the commit log. > > How about: > > --- > Add support for the Atheros AR300x Bluetooth Chip > > This adds support for the Atheros Bluetooth serial protocol to > support the AR300x chipsets. The serial protocol implements > enhanced power management features for the AR300x chipsets. > > Reviewed-by: Luis R. Rodriguez <lrodriguez@atheros.com> > Signed-off-by: Suraj <suraj@atheros.com> > --- > > Of course you will need to adjust the subject and prepend it with > PATCH v4, just as you did with a v3 for this patch. > > Then, this stuff: > >> * Third version >> >> ** Updated with extra spacing and indentation > > Do you use checkpatch.pl for your patches? If not please > add check your patches after committing them with: > > git show ./scripts/checkpatch.pl - > vi drivers/bluetooth/hci_ath.c > git commit -a --amend > > And repeat until checkpatch.pl is git happy :) Yes, I had done a checkpatch round with the patch. it looked happy. Did you see any issue that could should have been caught by it? > >> ** made function definitions static >> ** Removed inline and register keyword usage. >> ** Removed unused return calls. >> ** Incorporated code comments by Luis and Gustavo >> >> Thanks Luis and Gustavo for your comments > > > Remove it from the commit log, if you do want to add some extra text > to the patch youc an put it below the three dashes ("-") where the > diff stat goes: > >> >> Signed-off-by: Suraj <suraj@atheros.com> >> >> --- > ^^^ > > These are the three lines, anything below is ignored by git am > when the maintainer applies the patch. So you can add anything you > want ignored by git am here. > >> drivers/bluetooth/Kconfig | 11 ++ >> drivers/bluetooth/Makefile | 1 + >> drivers/bluetooth/hci_ath.c | 384 +++++++++++++++++++++++++++++++++++++++++ >> drivers/bluetooth/hci_ldisc.c | 6 + >> drivers/bluetooth/hci_uart.h | 8 +- >> 5 files changed, 409 insertions(+), 1 deletions(-) >> create mode 100755 drivers/bluetooth/hci_ath.c >> >> diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig >> index 058fbcc..81abeff 100644 >> --- a/drivers/bluetooth/Kconfig >> +++ b/drivers/bluetooth/Kconfig >> @@ -58,6 +58,17 @@ config BT_HCIUART_BCSP >> >> Say Y here to compile support for HCI BCSP protocol. >> >> +config BT_HCIUART_ATH >> + bool "Atheros AR300x Board support" >> + depends on BT_HCIUART >> + help >> + HCIATH (HCI Atheros) is a serial protocol for communication >> + between Bluetooth device and host with support for Atheros AR300x >> + power management feature. This protocol is required for >> + serial Bluetooth devices that are based on Atheros AR300x chips. > > Please adjust the description as well. How about: > > + HCIATH (HCI Atheros) is a serial protocol for communication > + between the host and Atheros AR300x Bluetooth devices. The > + protocol implements enhaned power management features for the > + the AR300x chipsets, it lets the controller chip go to sleep > + mode if there is no Bluetooth activity for some time and wakes > + up the chip in case of a Bluetooth activity. Enabling this > + option will build HCI Atheros support into the hci_uart driver. > + Enable this option if you have an UART Atheros AR300x serial > + device. > > >> + >> + Say Y here to compile support for HCIATH protocol. >> + >> config BT_HCIUART_LL >> bool "HCILL protocol support" >> depends on BT_HCIUART >> diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile >> index 7e5aed5..1481faa 100644 >> --- a/drivers/bluetooth/Makefile >> +++ b/drivers/bluetooth/Makefile >> @@ -26,4 +26,5 @@ hci_uart-y := hci_ldisc.o >> hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o >> hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o >> hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o >> +hci_uart-$(CONFIG_BT_HCIUART_ATH) += hci_ath.o >> hci_uart-objs := $(hci_uart-y) >> diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c >> new file mode 100755 >> index 0000000..2f91954 >> --- /dev/null >> +++ b/drivers/bluetooth/hci_ath.c >> @@ -0,0 +1,384 @@ >> +/* >> + * Copyright (c) 2009-2010 Atheros Communications Inc. >> + * >> + * 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 <linux/module.h> >> +#include <linux/kernel.h> >> + >> +#include <linux/init.h> >> +#include <linux/slab.h> >> +#include <linux/tty.h> >> +#include <linux/errno.h> >> +#include <linux/ioctl.h> >> +#include <linux/skbuff.h> >> + >> +#include <net/bluetooth/bluetooth.h> >> +#include <net/bluetooth/hci_core.h> >> + >> +#include "hci_uart.h" >> + >> + >> +/* HCIATH receiver States */ >> +#define HCIATH_W4_PACKET_TYPE 0 >> +#define HCIATH_W4_EVENT_HDR 1 >> +#define HCIATH_W4_ACL_HDR 2 >> +#define HCIATH_W4_SCO_HDR 3 >> +#define HCIATH_W4_DATA 4 >> + >> +struct ath_struct { >> + struct hci_uart *hu; >> + unsigned int rx_state; >> + unsigned int rx_count; >> + unsigned int cur_sleep; >> + >> + spinlock_t hciath_lock; >> + struct sk_buff *rx_skb; >> + struct sk_buff_head txq; >> + wait_queue_head_t wqevt; >> + struct work_struct ctxtsw; >> +}; >> + >> +static int ath_wakeup_ar3001(struct tty_struct *tty) >> +{ >> + struct termios settings; >> + int status = 0x00; >> + >> + status = tty->driver->ops->tiocmget(tty, NULL); >> + >> + if ((status & TIOCM_CTS)) >> + return status; > > No need for double () parens here. This should be fine: > > + if (status & TIOCM_CTS) > + return status; > > You would use double parens if you are doing a check against > another flag as well. > >> + >> + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); >> + >> + /* Disable Automatic RTSCTS */ >> + settings.c_cflag &= ~CRTSCTS; >> + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); >> + >> + status = tty->driver->ops->tiocmget(tty, NULL); >> + >> + /* Clear RTS first */ >> + tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS); >> + mdelay(20); >> + >> + status = tty->driver->ops->tiocmget(tty, NULL); >> + >> + /* Set RTS, wake up board */ >> + tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00); >> + mdelay(20); >> + >> + status = tty->driver->ops->tiocmget(tty, NULL); >> + >> + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); >> + >> + settings.c_cflag |= CRTSCTS; >> + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); >> + >> + return status; >> +} >> + >> +static void ath_context_switch(struct work_struct *work) > > Can you please rename this to ath_hci_uart_work() > >> +{ >> + int status; >> + struct ath_struct *ath; >> + struct hci_uart *hu; >> + struct tty_struct *tty; >> + >> + ath = container_of(work, struct ath_struct, ctxtsw); >> + >> + hu = ath->hu; >> + tty = hu->tty; >> + >> + /* verify and wake up controller */ >> + if (ath->cur_sleep) { >> + >> + status = ath_wakeup_ar3001(tty); >> + >> + if (!(status & TIOCM_CTS)) >> + return; >> + } >> + >> + /* Ready to send Data */ >> + clear_bit(HCI_UART_SENDING, &hu->tx_state); >> + hci_uart_tx_wakeup(hu); >> +} >> + >> +/* Initialize protocol */ >> +static int ath_open(struct hci_uart *hu) >> +{ >> + struct ath_struct *ath; >> + >> + BT_DBG("hu %p", hu); >> + >> + ath = kzalloc(sizeof(*ath), GFP_ATOMIC); >> + if (!ath) >> + return -ENOMEM; >> + >> + skb_queue_head_init(&ath->txq); >> + spin_lock_init(&ath->hciath_lock); >> + >> + hu->priv = ath; >> + ath->hu = hu; >> + >> + init_waitqueue_head(&ath->wqevt); >> + INIT_WORK(&ath->ctxtsw, ath_context_switch); >> + >> + return 0; >> +} >> + >> +/* Flush protocol data */ >> +static int ath_flush(struct hci_uart *hu) >> +{ >> + struct ath_struct *ath = hu->priv; >> + >> + BT_DBG("hu %p", hu); >> + >> + skb_queue_purge(&ath->txq); >> + >> + return 0; >> +} >> + >> +/* Close protocol */ >> +static int ath_close(struct hci_uart *hu) >> +{ >> + struct ath_struct *ath = hu->priv; >> + >> + BT_DBG("hu %p", hu); >> + >> + skb_queue_purge(&ath->txq); >> + >> + if (ath->rx_skb) >> + kfree_skb(ath->rx_skb); > > No need for the check, kfree_skb() does that for you > and it has it optimized for the case where the skb you > pass is NULL. You can just do: > > + kfree_skb(ath->rx_skb); > >> + >> + cancel_work_sync(&ath->ctxtsw); >> + >> + hu->priv = NULL; >> + kfree(ath); >> + >> + return 0; >> +} > > Did you give this new patch a spin by looping bringing up, > scanning, bringing the interface down? Or have a loop doing > a scan while in another window you bring the interface up > and down? > Yep, Have been testing it for sometime. Haven't seen any issue yet (Hope I dont see any :-) ) >> + >> +/* Enqueue frame for transmittion */ >> +static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) >> +{ >> + struct ath_struct *ath = hu->priv; >> + >> + if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { >> + >> + /* Discard SCO packet.AR3001 does not support SCO over HCI */ > > Add a space after packet. Why does it not support SCO over HCI BTW? > Just curious. I'm new to BT :) Is this common? If this is common can't > the BT stack be informed of these things so that they don't pass the skbs > to the driver? > From my experience, it is common for smaller BT devices (headsets) and devices with memory limitations. I guess, there is no way to tell SCO routing using any HCI cmd/Event. In the Linux Ubuntu distro that I use, it is controlled using the "SCORouting" flag in "/etc/bluetooth/audio.conf". Since it is a user configuration, driver can not assume that the flag will be set. So, this is like a precautionary check. > This could be done in a separate patch though if this is the case > though. > >> + BT_DBG("SCO Packet over HCI received Dropping"); >> + >> + kfree(skb); >> + >> + return 0; >> + } >> + >> + BT_DBG("hu %p skb %p", hu, skb); >> + >> + /* Prepend skb with frame type */ >> + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); >> + >> + skb_queue_tail(&ath->txq, skb); >> + set_bit(HCI_UART_SENDING, &hu->tx_state); >> + >> + schedule_work(&ath->ctxtsw); >> + >> + return 0; >> +} >> + >> +static struct sk_buff *ath_dequeue(struct hci_uart *hu) >> +{ >> + struct ath_struct *ath = hu->priv; >> + struct sk_buff *skbuf; >> + >> + skbuf = skb_dequeue(&ath->txq); >> + >> + if (!skbuf) >> + return NULL; >> + >> + >> + /* >> + * Check if the HCI command is HCI sleep enable and >> + * update the sleep enable flag with command parameter. >> + * >> + * Value of sleep enable flag will be used later >> + * to verify if controller has to be woken up before >> + * sending any packet. >> + */ >> + if (skbuf->data[0] == 0x01 && skbuf->data[1] == 0x04 && >> + skbuf->data[2] == 0xFC) >> + ath->cur_sleep = skbuf->data[4]; > > Might as well just do this: > > + if (skbuf->data[0] == 0x01 && > + skbuf->data[1] == 0x04 && > + skbuf->data[2] == 0xFC) > + ath->cur_sleep = skbuf->data[4]; > > Is this sort of check done in any other drivers/protocols? If so a > helper could be added to hci_uart.h if this is the case, but likely > better though a separate patch. No, this check is done only for this specific driver/protocol. This is the reason why we had to go for board specific driver change. Otherways, we could have used the default HCI driver implementation. > >> + >> + return skbuf; >> +} >> + >> +static void ath_check_data_len(struct ath_struct *ath, int len) >> +{ >> + int room = skb_tailroom(ath->rx_skb); >> + >> + BT_DBG("len %d room %d", len, room); >> + >> + if (len > room) { >> + BT_ERR("Data length is too large"); >> + kfree_skb(ath->rx_skb); >> + ath->rx_state = HCIATH_W4_PACKET_TYPE; >> + ath->rx_skb = NULL; >> + ath->rx_count = 0; >> + } else { >> + ath->rx_state = HCIATH_W4_DATA; >> + ath->rx_count = len; >> + } >> +} >> + >> +/* Recv data */ >> +static int ath_recv(struct hci_uart *hu, void *data, int count) >> +{ >> + struct ath_struct *ath = hu->priv; >> + char *ptr = data; >> + struct hci_event_hdr *eh; >> + struct hci_acl_hdr *ah; >> + struct hci_sco_hdr *sh; >> + int len, type, dlen; >> + >> + >> + BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, >> + ath->rx_state, ath->rx_count); >> + >> + while (count) { >> + if (ath->rx_count) { >> + >> + len = min_t(unsigned int, ath->rx_count, count); >> + memcpy(skb_put(ath->rx_skb, len), ptr, len); >> + ath->rx_count -= len; >> + count -= len; >> + ptr += len; >> + >> + if (ath->rx_count) >> + continue; >> + switch (ath->rx_state) { >> + case HCIATH_W4_DATA: >> + hci_recv_frame(ath->rx_skb); >> + ath->rx_state = HCIATH_W4_PACKET_TYPE; >> + ath->rx_skb = NULL; >> + ath->rx_count = 0; >> + continue; >> + >> + case HCIATH_W4_EVENT_HDR: >> + eh = (struct hci_event_hdr *)ath->rx_skb->data; >> + >> + BT_DBG("Event header: evt 0x%2.2x plen %d", >> + eh->evt, eh->plen); >> + >> + ath_check_data_len(ath, eh->plen); >> + continue; >> + >> + case HCIATH_W4_ACL_HDR: >> + ah = (struct hci_acl_hdr *)ath->rx_skb->data; >> + dlen = __le16_to_cpu(ah->dlen); >> + >> + BT_DBG("ACL header: dlen %d", dlen); >> + >> + ath_check_data_len(ath, dlen); >> + continue; >> + >> + case HCIATH_W4_SCO_HDR: >> + sh = (struct hci_sco_hdr *)ath->rx_skb->data; >> + >> + BT_DBG("SCO header: dlen %d", sh->dlen); >> + >> + ath_check_data_len(ath, sh->dlen); >> + continue; >> + >> + } >> + } >> + >> + /* HCIATH_W4_PACKET_TYPE */ >> + switch (*ptr) { >> + case HCI_EVENT_PKT: >> + BT_DBG("Event packet"); >> + ath->rx_state = HCIATH_W4_EVENT_HDR; >> + ath->rx_count = HCI_EVENT_HDR_SIZE; >> + type = HCI_EVENT_PKT; >> + break; >> + >> + case HCI_ACLDATA_PKT: >> + BT_DBG("ACL packet"); >> + ath->rx_state = HCIATH_W4_ACL_HDR; >> + ath->rx_count = HCI_ACL_HDR_SIZE; >> + type = HCI_ACLDATA_PKT; >> + break; >> + >> + case HCI_SCODATA_PKT: >> + BT_DBG("SCO packet"); >> + ath->rx_state = HCIATH_W4_SCO_HDR; >> + ath->rx_count = HCI_SCO_HDR_SIZE; >> + type = HCI_SCODATA_PKT; >> + break; >> + >> + default: >> + BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); >> + hu->hdev->stat.err_rx++; >> + ptr++; >> + count--; >> + continue; >> + >> + }; >> + ptr++; >> + count--; >> + >> + /* Allocate packet */ >> + ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); >> + if (!ath->rx_skb) { >> + BT_ERR("Can't allocate mem for new packet"); >> + ath->rx_state = HCIATH_W4_PACKET_TYPE; >> + ath->rx_count = 0; >> + >> + return -ENOMEM; >> + } >> + ath->rx_skb->dev = (void *)hu->hdev; >> + bt_cb(ath->rx_skb)->pkt_type = type; >> + } >> + >> + return count; >> +} >> + >> +static struct hci_uart_proto athp = { >> + .id = HCI_UART_ATH, >> + .open = ath_open, >> + .close = ath_close, >> + .recv = ath_recv, >> + .enqueue = ath_enqueue, >> + .dequeue = ath_dequeue, >> + .flush = ath_flush, >> +}; >> + >> +int ath_init(void) >> +{ >> + int err = hci_uart_register_proto(&athp); >> + >> + if (!err) >> + BT_INFO("HCIATH protocol initialized"); >> + else >> + BT_ERR("HCIATH protocol registration failed"); >> + >> + return err; >> +} >> + >> +int ath_deinit(void) >> +{ >> + return hci_uart_unregister_proto(&athp); >> +} >> diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c >> index 76a1abb..7dd76d1 100644 >> --- a/drivers/bluetooth/hci_ldisc.c >> +++ b/drivers/bluetooth/hci_ldisc.c >> @@ -542,6 +542,9 @@ static int __init hci_uart_init(void) >> #ifdef CONFIG_BT_HCIUART_LL >> ll_init(); >> #endif >> +#ifdef CONFIG_BT_HCIUART_ATH >> + ath_init(); >> +#endif >> >> return 0; >> } >> @@ -559,6 +562,9 @@ static void __exit hci_uart_exit(void) >> #ifdef CONFIG_BT_HCIUART_LL >> ll_deinit(); >> #endif >> +#ifdef CONFIG_BT_HCIUART_ATH >> + ath_deinit(); >> +#endif >> >> /* Release tty registration of line discipline */ >> if ((err = tty_unregister_ldisc(N_HCI))) >> diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h >> index 50113db..385537f 100644 >> --- a/drivers/bluetooth/hci_uart.h >> +++ b/drivers/bluetooth/hci_uart.h >> @@ -33,13 +33,14 @@ >> #define HCIUARTGETDEVICE _IOR('U', 202, int) >> >> /* UART protocols */ >> -#define HCI_UART_MAX_PROTO 5 >> +#define HCI_UART_MAX_PROTO 6 >> >> #define HCI_UART_H4 0 >> #define HCI_UART_BCSP 1 >> #define HCI_UART_3WIRE 2 >> #define HCI_UART_H4DS 3 >> #define HCI_UART_LL 4 >> +#define HCI_UART_ATH 5 >> >> struct hci_uart; >> >> @@ -91,3 +92,8 @@ int bcsp_deinit(void); >> int ll_init(void); >> int ll_deinit(void); >> #endif >> + >> +#ifdef CONFIG_BT_HCIUART_ATH >> +int ath_init(void); >> +int ath_deinit(void); >> +#endif >> -- >> 1.7.0 >> >> >> Thanks for the comments. Regards, Suraj ^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v4] Add support for the Atheros AR300x Bluetooth Chip 2010-04-20 10:20 ` [PATCH v3] " suraj 2010-04-20 15:36 ` Gustavo F. Padovan 2010-04-20 17:34 ` Luis R. Rodriguez @ 2010-04-21 10:22 ` suraj 2010-04-21 17:30 ` Luis R. Rodriguez ` (2 more replies) 2 siblings, 3 replies; 36+ messages in thread From: suraj @ 2010-04-21 10:22 UTC (permalink / raw) To: linux-bluetooth; +Cc: marcel, Luis.Rodriguez, Jothikumar.Mothilal, gfpadovan This implements the Atheros Bluetooth serial protocol to support the AR300x Bluetooth chipsets. The serial protocol implements enhanced power management features for the AR300x chipsets. Reviewed-by: Luis R. Rodriguez <lrodriguez@atheros.com> Signed-off-by: Suraj <suraj@atheros.com> --- drivers/bluetooth/Kconfig | 14 ++ drivers/bluetooth/Makefile | 1 + drivers/bluetooth/hci_ath.c | 378 +++++++++++++++++++++++++++++++++++++++++ drivers/bluetooth/hci_ldisc.c | 6 + drivers/bluetooth/hci_uart.h | 8 +- 5 files changed, 406 insertions(+), 1 deletions(-) create mode 100755 drivers/bluetooth/hci_ath.c diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 058fbcc..5546142 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -58,6 +58,20 @@ config BT_HCIUART_BCSP Say Y here to compile support for HCI BCSP protocol. +config BT_HCIUART_ATH + bool "Atheros AR300x serial Bluetooth support" + depends on BT_HCIUART + help + HCIATH (HCI Atheros) is a serial protocol for communication + between the host and Atheros AR300x Bluetooth devices. The + protocol implements enhaned power management features for the + the AR300x chipsets. it lets the controller chip go to sleep + mode if there is no Bluetooth activity for some time and wakes + up the chip in case of a Bluetooth activity. Enable this option + if you have an UART Atheros AR300x serial device. + + Say Y here to compile support for HCIATH protocol. + config BT_HCIUART_LL bool "HCILL protocol support" depends on BT_HCIUART diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 7e5aed5..1481faa 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -26,4 +26,5 @@ hci_uart-y := hci_ldisc.o hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o +hci_uart-$(CONFIG_BT_HCIUART_ATH) += hci_ath.o hci_uart-objs := $(hci_uart-y) diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c new file mode 100755 index 0000000..7e99559 --- /dev/null +++ b/drivers/bluetooth/hci_ath.c @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2009-2010 Atheros Communications Inc. + * + * 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 <linux/module.h> +#include <linux/kernel.h> + +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/errno.h> +#include <linux/ioctl.h> +#include <linux/skbuff.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +#include "hci_uart.h" + +/* HCIATH receiver States */ +#define HCIATH_W4_PACKET_TYPE 0 +#define HCIATH_W4_EVENT_HDR 1 +#define HCIATH_W4_ACL_HDR 2 +#define HCIATH_W4_SCO_HDR 3 +#define HCIATH_W4_DATA 4 + +struct ath_struct { + struct hci_uart *hu; + unsigned int rx_state; + unsigned int rx_count; + unsigned int cur_sleep; + + spinlock_t hciath_lock; + struct sk_buff *rx_skb; + struct sk_buff_head txq; + wait_queue_head_t wqevt; + struct work_struct ctxtsw; +}; + +static int ath_wakeup_ar3001(struct tty_struct *tty) +{ + struct termios settings; + int status = tty->driver->ops->tiocmget(tty, NULL); + + if (status & TIOCM_CTS) + return status; + + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); + + /* Disable Automatic RTSCTS */ + settings.c_cflag &= ~CRTSCTS; + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); + + status = tty->driver->ops->tiocmget(tty, NULL); + + /* Clear RTS first */ + tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS); + mdelay(20); + + status = tty->driver->ops->tiocmget(tty, NULL); + + /* Set RTS, wake up board */ + tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00); + mdelay(20); + + status = tty->driver->ops->tiocmget(tty, NULL); + + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); + + settings.c_cflag |= CRTSCTS; + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); + + return status; +} + +static void ath_hci_uart_work(struct work_struct *work) +{ + int status; + struct ath_struct *ath; + struct hci_uart *hu; + struct tty_struct *tty; + + ath = container_of(work, struct ath_struct, ctxtsw); + + hu = ath->hu; + tty = hu->tty; + + /* verify and wake up controller */ + if (ath->cur_sleep) { + status = ath_wakeup_ar3001(tty); + + if (!(status & TIOCM_CTS)) + return; + } + + /* Ready to send Data */ + clear_bit(HCI_UART_SENDING, &hu->tx_state); + hci_uart_tx_wakeup(hu); +} + +/* Initialize protocol */ +static int ath_open(struct hci_uart *hu) +{ + struct ath_struct *ath; + + BT_DBG("hu %p", hu); + + ath = kzalloc(sizeof(*ath), GFP_ATOMIC); + if (!ath) + return -ENOMEM; + + skb_queue_head_init(&ath->txq); + spin_lock_init(&ath->hciath_lock); + + hu->priv = ath; + ath->hu = hu; + + init_waitqueue_head(&ath->wqevt); + INIT_WORK(&ath->ctxtsw, ath_hci_uart_work); + + return 0; +} + +/* Flush protocol data */ +static int ath_flush(struct hci_uart *hu) +{ + struct ath_struct *ath = hu->priv; + + BT_DBG("hu %p", hu); + + skb_queue_purge(&ath->txq); + + return 0; +} + +/* Close protocol */ +static int ath_close(struct hci_uart *hu) +{ + struct ath_struct *ath = hu->priv; + + BT_DBG("hu %p", hu); + + skb_queue_purge(&ath->txq); + + kfree_skb(ath->rx_skb); + + cancel_work_sync(&ath->ctxtsw); + + hu->priv = NULL; + kfree(ath); + + return 0; +} + +/* Enqueue frame for transmittion */ +static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) +{ + struct ath_struct *ath = hu->priv; + + if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { + + /* Discard SCO packet. AR3001 does not support SCO over HCI */ + BT_DBG("SCO Packet over HCI received Dropping"); + + kfree(skb); + + return 0; + } + + BT_DBG("hu %p skb %p", hu, skb); + + /* Prepend skb with frame type */ + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + + skb_queue_tail(&ath->txq, skb); + set_bit(HCI_UART_SENDING, &hu->tx_state); + + schedule_work(&ath->ctxtsw); + + return 0; +} + +static struct sk_buff *ath_dequeue(struct hci_uart *hu) +{ + struct ath_struct *ath = hu->priv; + struct sk_buff *skbuf; + + skbuf = skb_dequeue(&ath->txq); + + if (!skbuf) + return NULL; + + /* + * Check if the HCI command is HCI sleep enable and + * update the sleep enable flag with command parameter. + * + * Value of sleep enable flag will be used later + * to verify if controller has to be woken up before + * sending any packet. + */ + if (skbuf->data[0] == 0x01 && + skbuf->data[1] == 0x04 && + skbuf->data[2] == 0xFC) + ath->cur_sleep = skbuf->data[4]; + + return skbuf; +} + +static void ath_check_data_len(struct ath_struct *ath, int len) +{ + int room = skb_tailroom(ath->rx_skb); + + BT_DBG("len %d room %d", len, room); + + if (len > room) { + BT_ERR("Data length is too large"); + kfree_skb(ath->rx_skb); + ath->rx_state = HCIATH_W4_PACKET_TYPE; + ath->rx_skb = NULL; + ath->rx_count = 0; + } else { + ath->rx_state = HCIATH_W4_DATA; + ath->rx_count = len; + } +} + +/* Recv data */ +static int ath_recv(struct hci_uart *hu, void *data, int count) +{ + struct ath_struct *ath = hu->priv; + char *ptr = data; + struct hci_event_hdr *eh; + struct hci_acl_hdr *ah; + struct hci_sco_hdr *sh; + int len, type, dlen; + + BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, + ath->rx_state, ath->rx_count); + + while (count) { + if (ath->rx_count) { + + len = min_t(unsigned int, ath->rx_count, count); + memcpy(skb_put(ath->rx_skb, len), ptr, len); + ath->rx_count -= len; + count -= len; + ptr += len; + + if (ath->rx_count) + continue; + switch (ath->rx_state) { + case HCIATH_W4_DATA: + hci_recv_frame(ath->rx_skb); + ath->rx_state = HCIATH_W4_PACKET_TYPE; + ath->rx_skb = NULL; + ath->rx_count = 0; + continue; + + case HCIATH_W4_EVENT_HDR: + eh = (struct hci_event_hdr *)ath->rx_skb->data; + + BT_DBG("Event header: evt 0x%2.2x plen %d", + eh->evt, eh->plen); + + ath_check_data_len(ath, eh->plen); + continue; + + case HCIATH_W4_ACL_HDR: + ah = (struct hci_acl_hdr *)ath->rx_skb->data; + dlen = __le16_to_cpu(ah->dlen); + + BT_DBG("ACL header: dlen %d", dlen); + + ath_check_data_len(ath, dlen); + continue; + + case HCIATH_W4_SCO_HDR: + sh = (struct hci_sco_hdr *)ath->rx_skb->data; + + BT_DBG("SCO header: dlen %d", sh->dlen); + + ath_check_data_len(ath, sh->dlen); + continue; + + } + } + + /* HCIATH_W4_PACKET_TYPE */ + switch (*ptr) { + case HCI_EVENT_PKT: + BT_DBG("Event packet"); + ath->rx_state = HCIATH_W4_EVENT_HDR; + ath->rx_count = HCI_EVENT_HDR_SIZE; + type = HCI_EVENT_PKT; + break; + + case HCI_ACLDATA_PKT: + BT_DBG("ACL packet"); + ath->rx_state = HCIATH_W4_ACL_HDR; + ath->rx_count = HCI_ACL_HDR_SIZE; + type = HCI_ACLDATA_PKT; + break; + + case HCI_SCODATA_PKT: + BT_DBG("SCO packet"); + ath->rx_state = HCIATH_W4_SCO_HDR; + ath->rx_count = HCI_SCO_HDR_SIZE; + type = HCI_SCODATA_PKT; + break; + + default: + BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); + hu->hdev->stat.err_rx++; + ptr++; + count--; + continue; + + }; + ptr++; + count--; + + /* Allocate packet */ + ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + if (!ath->rx_skb) { + BT_ERR("Can't allocate mem for new packet"); + ath->rx_state = HCIATH_W4_PACKET_TYPE; + ath->rx_count = 0; + + return -ENOMEM; + } + ath->rx_skb->dev = (void *)hu->hdev; + bt_cb(ath->rx_skb)->pkt_type = type; + } + + return count; +} + +static struct hci_uart_proto athp = { + .id = HCI_UART_ATH, + .open = ath_open, + .close = ath_close, + .recv = ath_recv, + .enqueue = ath_enqueue, + .dequeue = ath_dequeue, + .flush = ath_flush, +}; + +int ath_init(void) +{ + int err = hci_uart_register_proto(&athp); + + if (!err) + BT_INFO("HCIATH protocol initialized"); + else + BT_ERR("HCIATH protocol registration failed"); + + return err; +} + +int ath_deinit(void) +{ + return hci_uart_unregister_proto(&athp); +} diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 76a1abb..7dd76d1 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -542,6 +542,9 @@ static int __init hci_uart_init(void) #ifdef CONFIG_BT_HCIUART_LL ll_init(); #endif +#ifdef CONFIG_BT_HCIUART_ATH + ath_init(); +#endif return 0; } @@ -559,6 +562,9 @@ static void __exit hci_uart_exit(void) #ifdef CONFIG_BT_HCIUART_LL ll_deinit(); #endif +#ifdef CONFIG_BT_HCIUART_ATH + ath_deinit(); +#endif /* Release tty registration of line discipline */ if ((err = tty_unregister_ldisc(N_HCI))) diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 50113db..385537f 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -33,13 +33,14 @@ #define HCIUARTGETDEVICE _IOR('U', 202, int) /* UART protocols */ -#define HCI_UART_MAX_PROTO 5 +#define HCI_UART_MAX_PROTO 6 #define HCI_UART_H4 0 #define HCI_UART_BCSP 1 #define HCI_UART_3WIRE 2 #define HCI_UART_H4DS 3 #define HCI_UART_LL 4 +#define HCI_UART_ATH 5 struct hci_uart; @@ -91,3 +92,8 @@ int bcsp_deinit(void); int ll_init(void); int ll_deinit(void); #endif + +#ifdef CONFIG_BT_HCIUART_ATH +int ath_init(void); +int ath_deinit(void); +#endif -- 1.7.0 ^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [PATCH v4] Add support for the Atheros AR300x Bluetooth Chip 2010-04-21 10:22 ` [PATCH v4] Add support for the " suraj @ 2010-04-21 17:30 ` Luis R. Rodriguez 2010-04-22 6:10 ` Gustavo F. Padovan 2010-04-22 9:10 ` [PATCH v5] " suraj 2 siblings, 0 replies; 36+ messages in thread From: Luis R. Rodriguez @ 2010-04-21 17:30 UTC (permalink / raw) To: Suraj Sumangala Cc: linux-bluetooth@vger.kernel.org, marcel@holtmann.org, Luis Rodriguez, Jothikumar Mothilal, gfpadovan@gmail.com On Wed, Apr 21, 2010 at 03:22:17AM -0700, Suraj Sumangala wrote: > This implements the Atheros Bluetooth serial protocol to > support the AR300x Bluetooth chipsets. > The serial protocol implements enhanced power management > features for the AR300x chipsets. > > Reviewed-by: Luis R. Rodriguez <lrodriguez@atheros.com> > Signed-off-by: Suraj <suraj@atheros.com> Looks good to me now, thanks for your patience and the resends. Marcel? Luis ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v4] Add support for the Atheros AR300x Bluetooth Chip 2010-04-21 10:22 ` [PATCH v4] Add support for the " suraj 2010-04-21 17:30 ` Luis R. Rodriguez @ 2010-04-22 6:10 ` Gustavo F. Padovan 2010-04-22 6:54 ` Suraj Sumangala 2010-04-22 9:10 ` [PATCH v5] " suraj 2 siblings, 1 reply; 36+ messages in thread From: Gustavo F. Padovan @ 2010-04-22 6:10 UTC (permalink / raw) To: suraj; +Cc: linux-bluetooth, marcel, Luis.Rodriguez, Jothikumar.Mothilal * suraj <suraj@atheros.com> [2010-04-21 15:52:17 +0530]: > This implements the Atheros Bluetooth serial protocol to > support the AR300x Bluetooth chipsets. > The serial protocol implements enhanced power management > features for the AR300x chipsets. > > Reviewed-by: Luis R. Rodriguez <lrodriguez@atheros.com> > Signed-off-by: Suraj <suraj@atheros.com> > > --- > drivers/bluetooth/Kconfig | 14 ++ > drivers/bluetooth/Makefile | 1 + > drivers/bluetooth/hci_ath.c | 378 +++++++++++++++++++++++++++++++++++++++++ > drivers/bluetooth/hci_ldisc.c | 6 + > drivers/bluetooth/hci_uart.h | 8 +- > 5 files changed, 406 insertions(+), 1 deletions(-) > create mode 100755 drivers/bluetooth/hci_ath.c > > diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig > index 058fbcc..5546142 100644 > --- a/drivers/bluetooth/Kconfig > +++ b/drivers/bluetooth/Kconfig > @@ -58,6 +58,20 @@ config BT_HCIUART_BCSP > > Say Y here to compile support for HCI BCSP protocol. > > +config BT_HCIUART_ATH > + bool "Atheros AR300x serial Bluetooth support" > + depends on BT_HCIUART > + help > + HCIATH (HCI Atheros) is a serial protocol for communication > + between the host and Atheros AR300x Bluetooth devices. The > + protocol implements enhaned power management features for the > + the AR300x chipsets. it lets the controller chip go to sleep > + mode if there is no Bluetooth activity for some time and wakes > + up the chip in case of a Bluetooth activity. Enable this option > + if you have an UART Atheros AR300x serial device. > + > + Say Y here to compile support for HCIATH protocol. > + > config BT_HCIUART_LL > bool "HCILL protocol support" > depends on BT_HCIUART > diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile > index 7e5aed5..1481faa 100644 > --- a/drivers/bluetooth/Makefile > +++ b/drivers/bluetooth/Makefile > @@ -26,4 +26,5 @@ hci_uart-y := hci_ldisc.o > hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o > hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o > hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o > +hci_uart-$(CONFIG_BT_HCIUART_ATH) += hci_ath.o > hci_uart-objs := $(hci_uart-y) > diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c > new file mode 100755 > index 0000000..7e99559 > --- /dev/null > +++ b/drivers/bluetooth/hci_ath.c > @@ -0,0 +1,378 @@ > +/* > + * Copyright (c) 2009-2010 Atheros Communications Inc. > + * > + * 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 <linux/module.h> > +#include <linux/kernel.h> > + > +#include <linux/init.h> > +#include <linux/slab.h> > +#include <linux/tty.h> > +#include <linux/errno.h> > +#include <linux/ioctl.h> > +#include <linux/skbuff.h> > + > +#include <net/bluetooth/bluetooth.h> > +#include <net/bluetooth/hci_core.h> > + > +#include "hci_uart.h" > + > +/* HCIATH receiver States */ > +#define HCIATH_W4_PACKET_TYPE 0 > +#define HCIATH_W4_EVENT_HDR 1 > +#define HCIATH_W4_ACL_HDR 2 > +#define HCIATH_W4_SCO_HDR 3 > +#define HCIATH_W4_DATA 4 > + > +struct ath_struct { > + struct hci_uart *hu; > + unsigned int rx_state; > + unsigned int rx_count; > + unsigned int cur_sleep; > + > + spinlock_t hciath_lock; > + struct sk_buff *rx_skb; > + struct sk_buff_head txq; > + wait_queue_head_t wqevt; > + struct work_struct ctxtsw; > +}; > + > +static int ath_wakeup_ar3001(struct tty_struct *tty) > +{ > + struct termios settings; > + int status = tty->driver->ops->tiocmget(tty, NULL); > + > + if (status & TIOCM_CTS) > + return status; > + > + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); > + > + /* Disable Automatic RTSCTS */ > + settings.c_cflag &= ~CRTSCTS; > + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + /* Clear RTS first */ > + tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS); > + mdelay(20); > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + /* Set RTS, wake up board */ > + tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00); > + mdelay(20); > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); > + > + settings.c_cflag |= CRTSCTS; > + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); > + > + return status; > +} > + > +static void ath_hci_uart_work(struct work_struct *work) > +{ > + int status; > + struct ath_struct *ath; > + struct hci_uart *hu; > + struct tty_struct *tty; > + > + ath = container_of(work, struct ath_struct, ctxtsw); > + > + hu = ath->hu; > + tty = hu->tty; > + > + /* verify and wake up controller */ > + if (ath->cur_sleep) { > + status = ath_wakeup_ar3001(tty); > + > + if (!(status & TIOCM_CTS)) > + return; > + } > + > + /* Ready to send Data */ > + clear_bit(HCI_UART_SENDING, &hu->tx_state); > + hci_uart_tx_wakeup(hu); > +} > + > +/* Initialize protocol */ > +static int ath_open(struct hci_uart *hu) > +{ > + struct ath_struct *ath; > + > + BT_DBG("hu %p", hu); > + > + ath = kzalloc(sizeof(*ath), GFP_ATOMIC); > + if (!ath) > + return -ENOMEM; > + > + skb_queue_head_init(&ath->txq); > + spin_lock_init(&ath->hciath_lock); > + > + hu->priv = ath; > + ath->hu = hu; > + > + init_waitqueue_head(&ath->wqevt); > + INIT_WORK(&ath->ctxtsw, ath_hci_uart_work); > + > + return 0; > +} > + > +/* Flush protocol data */ > +static int ath_flush(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + > + BT_DBG("hu %p", hu); > + > + skb_queue_purge(&ath->txq); > + > + return 0; > +} > + > +/* Close protocol */ > +static int ath_close(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + > + BT_DBG("hu %p", hu); > + > + skb_queue_purge(&ath->txq); > + > + kfree_skb(ath->rx_skb); > + > + cancel_work_sync(&ath->ctxtsw); > + > + hu->priv = NULL; > + kfree(ath); > + > + return 0; > +} > + > +/* Enqueue frame for transmittion */ > +static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) > +{ > + struct ath_struct *ath = hu->priv; > + > + if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { > + > + /* Discard SCO packet. AR3001 does not support SCO over HCI */ > + BT_DBG("SCO Packet over HCI received Dropping"); > + > + kfree(skb); > + > + return 0; > + } > + > + BT_DBG("hu %p skb %p", hu, skb); > + > + /* Prepend skb with frame type */ > + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); > + > + skb_queue_tail(&ath->txq, skb); > + set_bit(HCI_UART_SENDING, &hu->tx_state); > + > + schedule_work(&ath->ctxtsw); > + > + return 0; > +} > + > +static struct sk_buff *ath_dequeue(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + struct sk_buff *skbuf; > + > + skbuf = skb_dequeue(&ath->txq); > + > + if (!skbuf) > + return NULL; > + > + /* > + * Check if the HCI command is HCI sleep enable and > + * update the sleep enable flag with command parameter. > + * > + * Value of sleep enable flag will be used later > + * to verify if controller has to be woken up before > + * sending any packet. > + */ > + if (skbuf->data[0] == 0x01 && > + skbuf->data[1] == 0x04 && > + skbuf->data[2] == 0xFC) > + ath->cur_sleep = skbuf->data[4]; > + > + return skbuf; > +} > + > +static void ath_check_data_len(struct ath_struct *ath, int len) > +{ > + int room = skb_tailroom(ath->rx_skb); > + > + BT_DBG("len %d room %d", len, room); > + > + if (len > room) { > + BT_ERR("Data length is too large"); > + kfree_skb(ath->rx_skb); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_skb = NULL; > + ath->rx_count = 0; > + } else { > + ath->rx_state = HCIATH_W4_DATA; > + ath->rx_count = len; > + } > +} > + > +/* Recv data */ > +static int ath_recv(struct hci_uart *hu, void *data, int count) > +{ > + struct ath_struct *ath = hu->priv; > + char *ptr = data; > + struct hci_event_hdr *eh; > + struct hci_acl_hdr *ah; > + struct hci_sco_hdr *sh; > + int len, type, dlen; > + > + BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, > + ath->rx_state, ath->rx_count); > + > + while (count) { > + if (ath->rx_count) { > + > + len = min_t(unsigned int, ath->rx_count, count); > + memcpy(skb_put(ath->rx_skb, len), ptr, len); > + ath->rx_count -= len; > + count -= len; > + ptr += len; > + > + if (ath->rx_count) > + continue; > + switch (ath->rx_state) { > + case HCIATH_W4_DATA: > + hci_recv_frame(ath->rx_skb); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_skb = NULL; > + ath->rx_count = 0; > + continue; > + > + case HCIATH_W4_EVENT_HDR: > + eh = (struct hci_event_hdr *)ath->rx_skb->data; Use hci_event_hdr() here like hci_h4.c does. > + > + BT_DBG("Event header: evt 0x%2.2x plen %d", > + eh->evt, eh->plen); > + > + ath_check_data_len(ath, eh->plen); > + continue; > + > + case HCIATH_W4_ACL_HDR: > + ah = (struct hci_acl_hdr *)ath->rx_skb->data; And hci_acl_hdr() here. > + dlen = __le16_to_cpu(ah->dlen); > + > + BT_DBG("ACL header: dlen %d", dlen); > + > + ath_check_data_len(ath, dlen); > + continue; > + > + case HCIATH_W4_SCO_HDR: > + sh = (struct hci_sco_hdr *)ath->rx_skb->data; hci_sco_hdr() here. > + > + BT_DBG("SCO header: dlen %d", sh->dlen); > + > + ath_check_data_len(ath, sh->dlen); > + continue; > + > + } > + } > + > + /* HCIATH_W4_PACKET_TYPE */ > + switch (*ptr) { > + case HCI_EVENT_PKT: > + BT_DBG("Event packet"); > + ath->rx_state = HCIATH_W4_EVENT_HDR; > + ath->rx_count = HCI_EVENT_HDR_SIZE; > + type = HCI_EVENT_PKT; > + break; > + > + case HCI_ACLDATA_PKT: > + BT_DBG("ACL packet"); > + ath->rx_state = HCIATH_W4_ACL_HDR; > + ath->rx_count = HCI_ACL_HDR_SIZE; > + type = HCI_ACLDATA_PKT; > + break; > + > + case HCI_SCODATA_PKT: > + BT_DBG("SCO packet"); > + ath->rx_state = HCIATH_W4_SCO_HDR; > + ath->rx_count = HCI_SCO_HDR_SIZE; > + type = HCI_SCODATA_PKT; > + break; > + > + default: > + BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); > + hu->hdev->stat.err_rx++; > + ptr++; > + count--; > + continue; > + > + }; > + ptr++; > + count--; > + > + /* Allocate packet */ > + ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); > + if (!ath->rx_skb) { > + BT_ERR("Can't allocate mem for new packet"); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_count = 0; > + > + return -ENOMEM; > + } > + ath->rx_skb->dev = (void *)hu->hdev; > + bt_cb(ath->rx_skb)->pkt_type = type; > + } > + > + return count; > +} Just out of curiosity. I never worked in the driver world, but is it ok to duplicate lots of code when working with drivers? hci_h4.c and this patch share lots of similar code. ath_recv() and h4_recv() are exactly the same except for one line, the ath->rx_count = 0; at case HCIATH_W4_DATA. Does this line makes real difference? recv() is not the only function duplicating code here. > + > +static struct hci_uart_proto athp = { > + .id = HCI_UART_ATH, > + .open = ath_open, > + .close = ath_close, > + .recv = ath_recv, > + .enqueue = ath_enqueue, > + .dequeue = ath_dequeue, > + .flush = ath_flush, > +}; > + > +int ath_init(void) > +{ > + int err = hci_uart_register_proto(&athp); > + > + if (!err) > + BT_INFO("HCIATH protocol initialized"); > + else > + BT_ERR("HCIATH protocol registration failed"); > + > + return err; > +} BTW, we never check this return value on hci_ldisc.c, why? > + > +int ath_deinit(void) > +{ > + return hci_uart_unregister_proto(&athp); > +} > diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c > index 76a1abb..7dd76d1 100644 > --- a/drivers/bluetooth/hci_ldisc.c > +++ b/drivers/bluetooth/hci_ldisc.c > @@ -542,6 +542,9 @@ static int __init hci_uart_init(void) > #ifdef CONFIG_BT_HCIUART_LL > ll_init(); > #endif > +#ifdef CONFIG_BT_HCIUART_ATH > + ath_init(); > +#endif > > return 0; > } > @@ -559,6 +562,9 @@ static void __exit hci_uart_exit(void) > #ifdef CONFIG_BT_HCIUART_LL > ll_deinit(); > #endif > +#ifdef CONFIG_BT_HCIUART_ATH > + ath_deinit(); > +#endif > > /* Release tty registration of line discipline */ > if ((err = tty_unregister_ldisc(N_HCI))) > diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h > index 50113db..385537f 100644 > --- a/drivers/bluetooth/hci_uart.h > +++ b/drivers/bluetooth/hci_uart.h > @@ -33,13 +33,14 @@ > #define HCIUARTGETDEVICE _IOR('U', 202, int) > > /* UART protocols */ > -#define HCI_UART_MAX_PROTO 5 > +#define HCI_UART_MAX_PROTO 6 > > #define HCI_UART_H4 0 > #define HCI_UART_BCSP 1 > #define HCI_UART_3WIRE 2 > #define HCI_UART_H4DS 3 > #define HCI_UART_LL 4 > +#define HCI_UART_ATH 5 > > struct hci_uart; > > @@ -91,3 +92,8 @@ int bcsp_deinit(void); > int ll_init(void); > int ll_deinit(void); > #endif > + > +#ifdef CONFIG_BT_HCIUART_ATH > +int ath_init(void); > +int ath_deinit(void); > +#endif > -- > 1.7.0 > > > > > -- Gustavo F. Padovan http://padovan.org ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v4] Add support for the Atheros AR300x Bluetooth Chip 2010-04-22 6:10 ` Gustavo F. Padovan @ 2010-04-22 6:54 ` Suraj Sumangala 2010-04-22 8:59 ` Gustavo F. Padovan 0 siblings, 1 reply; 36+ messages in thread From: Suraj Sumangala @ 2010-04-22 6:54 UTC (permalink / raw) To: Gustavo F. Padovan Cc: Suraj Sumangala, linux-bluetooth@vger.kernel.org, marcel@holtmann.org, Luis Rodriguez, Jothikumar Mothilal Hi Gustavo, Gustavo F. Padovan wrote: > * suraj <suraj@atheros.com> [2010-04-21 15:52:17 +0530]: > >> This implements the Atheros Bluetooth serial protocol to >> support the AR300x Bluetooth chipsets. >> The serial protocol implements enhanced power management >> features for the AR300x chipsets. >> >> Reviewed-by: Luis R. Rodriguez <lrodriguez@atheros.com> >> Signed-off-by: Suraj <suraj@atheros.com> >> >> --- >> drivers/bluetooth/Kconfig | 14 ++ >> drivers/bluetooth/Makefile | 1 + >> drivers/bluetooth/hci_ath.c | 378 +++++++++++++++++++++++++++++++++++++++++ >> drivers/bluetooth/hci_ldisc.c | 6 + >> drivers/bluetooth/hci_uart.h | 8 +- >> 5 files changed, 406 insertions(+), 1 deletions(-) >> create mode 100755 drivers/bluetooth/hci_ath.c >> >> diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig >> index 058fbcc..5546142 100644 >> --- a/drivers/bluetooth/Kconfig >> +++ b/drivers/bluetooth/Kconfig >> @@ -58,6 +58,20 @@ config BT_HCIUART_BCSP >> >> Say Y here to compile support for HCI BCSP protocol. >> >> +config BT_HCIUART_ATH >> + bool "Atheros AR300x serial Bluetooth support" >> + depends on BT_HCIUART >> + help >> + HCIATH (HCI Atheros) is a serial protocol for communication >> + between the host and Atheros AR300x Bluetooth devices. The >> + protocol implements enhaned power management features for the >> + the AR300x chipsets. it lets the controller chip go to sleep >> + mode if there is no Bluetooth activity for some time and wakes >> + up the chip in case of a Bluetooth activity. Enable this option >> + if you have an UART Atheros AR300x serial device. >> + >> + Say Y here to compile support for HCIATH protocol. >> + >> config BT_HCIUART_LL >> bool "HCILL protocol support" >> depends on BT_HCIUART >> diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile >> index 7e5aed5..1481faa 100644 >> --- a/drivers/bluetooth/Makefile >> +++ b/drivers/bluetooth/Makefile >> @@ -26,4 +26,5 @@ hci_uart-y := hci_ldisc.o >> hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o >> hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o >> hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o >> +hci_uart-$(CONFIG_BT_HCIUART_ATH) += hci_ath.o >> hci_uart-objs := $(hci_uart-y) >> diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c >> new file mode 100755 >> index 0000000..7e99559 >> --- /dev/null >> +++ b/drivers/bluetooth/hci_ath.c >> @@ -0,0 +1,378 @@ >> +/* >> + * Copyright (c) 2009-2010 Atheros Communications Inc. >> + * >> + * 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 <linux/module.h> >> +#include <linux/kernel.h> >> + >> +#include <linux/init.h> >> +#include <linux/slab.h> >> +#include <linux/tty.h> >> +#include <linux/errno.h> >> +#include <linux/ioctl.h> >> +#include <linux/skbuff.h> >> + >> +#include <net/bluetooth/bluetooth.h> >> +#include <net/bluetooth/hci_core.h> >> + >> +#include "hci_uart.h" >> + >> +/* HCIATH receiver States */ >> +#define HCIATH_W4_PACKET_TYPE 0 >> +#define HCIATH_W4_EVENT_HDR 1 >> +#define HCIATH_W4_ACL_HDR 2 >> +#define HCIATH_W4_SCO_HDR 3 >> +#define HCIATH_W4_DATA 4 >> + >> +struct ath_struct { >> + struct hci_uart *hu; >> + unsigned int rx_state; >> + unsigned int rx_count; >> + unsigned int cur_sleep; >> + >> + spinlock_t hciath_lock; >> + struct sk_buff *rx_skb; >> + struct sk_buff_head txq; >> + wait_queue_head_t wqevt; >> + struct work_struct ctxtsw; >> +}; >> + >> +static int ath_wakeup_ar3001(struct tty_struct *tty) >> +{ >> + struct termios settings; >> + int status = tty->driver->ops->tiocmget(tty, NULL); >> + >> + if (status & TIOCM_CTS) >> + return status; >> + >> + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); >> + >> + /* Disable Automatic RTSCTS */ >> + settings.c_cflag &= ~CRTSCTS; >> + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); >> + >> + status = tty->driver->ops->tiocmget(tty, NULL); >> + >> + /* Clear RTS first */ >> + tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS); >> + mdelay(20); >> + >> + status = tty->driver->ops->tiocmget(tty, NULL); >> + >> + /* Set RTS, wake up board */ >> + tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00); >> + mdelay(20); >> + >> + status = tty->driver->ops->tiocmget(tty, NULL); >> + >> + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); >> + >> + settings.c_cflag |= CRTSCTS; >> + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); >> + >> + return status; >> +} >> + >> +static void ath_hci_uart_work(struct work_struct *work) >> +{ >> + int status; >> + struct ath_struct *ath; >> + struct hci_uart *hu; >> + struct tty_struct *tty; >> + >> + ath = container_of(work, struct ath_struct, ctxtsw); >> + >> + hu = ath->hu; >> + tty = hu->tty; >> + >> + /* verify and wake up controller */ >> + if (ath->cur_sleep) { >> + status = ath_wakeup_ar3001(tty); >> + >> + if (!(status & TIOCM_CTS)) >> + return; >> + } >> + >> + /* Ready to send Data */ >> + clear_bit(HCI_UART_SENDING, &hu->tx_state); >> + hci_uart_tx_wakeup(hu); >> +} >> + >> +/* Initialize protocol */ >> +static int ath_open(struct hci_uart *hu) >> +{ >> + struct ath_struct *ath; >> + >> + BT_DBG("hu %p", hu); >> + >> + ath = kzalloc(sizeof(*ath), GFP_ATOMIC); >> + if (!ath) >> + return -ENOMEM; >> + >> + skb_queue_head_init(&ath->txq); >> + spin_lock_init(&ath->hciath_lock); >> + >> + hu->priv = ath; >> + ath->hu = hu; >> + >> + init_waitqueue_head(&ath->wqevt); >> + INIT_WORK(&ath->ctxtsw, ath_hci_uart_work); >> + >> + return 0; >> +} >> + >> +/* Flush protocol data */ >> +static int ath_flush(struct hci_uart *hu) >> +{ >> + struct ath_struct *ath = hu->priv; >> + >> + BT_DBG("hu %p", hu); >> + >> + skb_queue_purge(&ath->txq); >> + >> + return 0; >> +} >> + >> +/* Close protocol */ >> +static int ath_close(struct hci_uart *hu) >> +{ >> + struct ath_struct *ath = hu->priv; >> + >> + BT_DBG("hu %p", hu); >> + >> + skb_queue_purge(&ath->txq); >> + >> + kfree_skb(ath->rx_skb); >> + >> + cancel_work_sync(&ath->ctxtsw); >> + >> + hu->priv = NULL; >> + kfree(ath); >> + >> + return 0; >> +} >> + >> +/* Enqueue frame for transmittion */ >> +static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) >> +{ >> + struct ath_struct *ath = hu->priv; >> + >> + if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { >> + >> + /* Discard SCO packet. AR3001 does not support SCO over HCI */ >> + BT_DBG("SCO Packet over HCI received Dropping"); >> + >> + kfree(skb); >> + >> + return 0; >> + } >> + >> + BT_DBG("hu %p skb %p", hu, skb); >> + >> + /* Prepend skb with frame type */ >> + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); >> + >> + skb_queue_tail(&ath->txq, skb); >> + set_bit(HCI_UART_SENDING, &hu->tx_state); >> + >> + schedule_work(&ath->ctxtsw); >> + >> + return 0; >> +} >> + >> +static struct sk_buff *ath_dequeue(struct hci_uart *hu) >> +{ >> + struct ath_struct *ath = hu->priv; >> + struct sk_buff *skbuf; >> + >> + skbuf = skb_dequeue(&ath->txq); >> + >> + if (!skbuf) >> + return NULL; >> + >> + /* >> + * Check if the HCI command is HCI sleep enable and >> + * update the sleep enable flag with command parameter. >> + * >> + * Value of sleep enable flag will be used later >> + * to verify if controller has to be woken up before >> + * sending any packet. >> + */ >> + if (skbuf->data[0] == 0x01 && >> + skbuf->data[1] == 0x04 && >> + skbuf->data[2] == 0xFC) >> + ath->cur_sleep = skbuf->data[4]; >> + >> + return skbuf; >> +} >> + >> +static void ath_check_data_len(struct ath_struct *ath, int len) >> +{ >> + int room = skb_tailroom(ath->rx_skb); >> + >> + BT_DBG("len %d room %d", len, room); >> + >> + if (len > room) { >> + BT_ERR("Data length is too large"); >> + kfree_skb(ath->rx_skb); >> + ath->rx_state = HCIATH_W4_PACKET_TYPE; >> + ath->rx_skb = NULL; >> + ath->rx_count = 0; >> + } else { >> + ath->rx_state = HCIATH_W4_DATA; >> + ath->rx_count = len; >> + } >> +} >> + >> +/* Recv data */ >> +static int ath_recv(struct hci_uart *hu, void *data, int count) >> +{ >> + struct ath_struct *ath = hu->priv; >> + char *ptr = data; >> + struct hci_event_hdr *eh; >> + struct hci_acl_hdr *ah; >> + struct hci_sco_hdr *sh; >> + int len, type, dlen; >> + >> + BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, >> + ath->rx_state, ath->rx_count); >> + >> + while (count) { >> + if (ath->rx_count) { >> + >> + len = min_t(unsigned int, ath->rx_count, count); >> + memcpy(skb_put(ath->rx_skb, len), ptr, len); >> + ath->rx_count -= len; >> + count -= len; >> + ptr += len; >> + >> + if (ath->rx_count) >> + continue; >> + switch (ath->rx_state) { >> + case HCIATH_W4_DATA: >> + hci_recv_frame(ath->rx_skb); >> + ath->rx_state = HCIATH_W4_PACKET_TYPE; >> + ath->rx_skb = NULL; >> + ath->rx_count = 0; >> + continue; >> + >> + case HCIATH_W4_EVENT_HDR: >> + eh = (struct hci_event_hdr *)ath->rx_skb->data; > > Use hci_event_hdr() here like hci_h4.c does. > >> + >> + BT_DBG("Event header: evt 0x%2.2x plen %d", >> + eh->evt, eh->plen); >> + >> + ath_check_data_len(ath, eh->plen); >> + continue; >> + >> + case HCIATH_W4_ACL_HDR: >> + ah = (struct hci_acl_hdr *)ath->rx_skb->data; > > And hci_acl_hdr() here. > >> + dlen = __le16_to_cpu(ah->dlen); >> + >> + BT_DBG("ACL header: dlen %d", dlen); >> + >> + ath_check_data_len(ath, dlen); >> + continue; >> + >> + case HCIATH_W4_SCO_HDR: >> + sh = (struct hci_sco_hdr *)ath->rx_skb->data; > > hci_sco_hdr() here. > >> + >> + BT_DBG("SCO header: dlen %d", sh->dlen); >> + >> + ath_check_data_len(ath, sh->dlen); >> + continue; >> + >> + } >> + } >> + >> + /* HCIATH_W4_PACKET_TYPE */ >> + switch (*ptr) { >> + case HCI_EVENT_PKT: >> + BT_DBG("Event packet"); >> + ath->rx_state = HCIATH_W4_EVENT_HDR; >> + ath->rx_count = HCI_EVENT_HDR_SIZE; >> + type = HCI_EVENT_PKT; >> + break; >> + >> + case HCI_ACLDATA_PKT: >> + BT_DBG("ACL packet"); >> + ath->rx_state = HCIATH_W4_ACL_HDR; >> + ath->rx_count = HCI_ACL_HDR_SIZE; >> + type = HCI_ACLDATA_PKT; >> + break; >> + >> + case HCI_SCODATA_PKT: >> + BT_DBG("SCO packet"); >> + ath->rx_state = HCIATH_W4_SCO_HDR; >> + ath->rx_count = HCI_SCO_HDR_SIZE; >> + type = HCI_SCODATA_PKT; >> + break; >> + >> + default: >> + BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); >> + hu->hdev->stat.err_rx++; >> + ptr++; >> + count--; >> + continue; >> + >> + }; >> + ptr++; >> + count--; >> + >> + /* Allocate packet */ >> + ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); >> + if (!ath->rx_skb) { >> + BT_ERR("Can't allocate mem for new packet"); >> + ath->rx_state = HCIATH_W4_PACKET_TYPE; >> + ath->rx_count = 0; >> + >> + return -ENOMEM; >> + } >> + ath->rx_skb->dev = (void *)hu->hdev; >> + bt_cb(ath->rx_skb)->pkt_type = type; >> + } >> + >> + return count; >> +} > > Just out of curiosity. I never worked in the driver world, but is it ok > to duplicate lots of code when working with drivers? hci_h4.c and this > patch share lots of similar code. ath_recv() and h4_recv() are exactly > the same except for one line, the ath->rx_count = 0; at case HCIATH_W4_DATA. > Does this line makes real difference? recv() is not the only function > duplicating code here. Let me try to anwer you here. I am also new to the driver world, so correct me if I am wrong. Both drivers do the same job, expect that the ATH driver does something more on the data transmit path. So, the receive path does the same thing. That is the reason why both looks same. This could possibly change later as new feature will be added to the ATH protocol. The function "hci_recv_fragment()" could potentially replace most of the code in the ath_recv() function. But, unfortunately this function require the caller to provide the HCI Packet type as parameter. This defeats all the advantage of "hci_recv_fragment()" in a UART HCI transport driver as all types of packets are received through the same callback. So the caller will have to write the same messy code in ath_recv() to find out the packet type negating all the advantages of "hci_recv_fragment()". > >> + >> +static struct hci_uart_proto athp = { >> + .id = HCI_UART_ATH, >> + .open = ath_open, >> + .close = ath_close, >> + .recv = ath_recv, >> + .enqueue = ath_enqueue, >> + .dequeue = ath_dequeue, >> + .flush = ath_flush, >> +}; >> + >> +int ath_init(void) >> +{ >> + int err = hci_uart_register_proto(&athp); >> + >> + if (!err) >> + BT_INFO("HCIATH protocol initialized"); >> + else >> + BT_ERR("HCIATH protocol registration failed"); >> + >> + return err; >> +} > > BTW, we never check this return value on hci_ldisc.c, why? you, are correct. Just thought of keeping the same signature used by other protocol. moreover hci_ldisc.c being a common file used by all protocol it is possible that somebody want to check the return value at later point of time. So, kept it that way so that we do not have a problem then :-D Your thoughts? > >> + >> +int ath_deinit(void) >> +{ >> + return hci_uart_unregister_proto(&athp); >> +} >> diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c >> index 76a1abb..7dd76d1 100644 >> --- a/drivers/bluetooth/hci_ldisc.c >> +++ b/drivers/bluetooth/hci_ldisc.c >> @@ -542,6 +542,9 @@ static int __init hci_uart_init(void) >> #ifdef CONFIG_BT_HCIUART_LL >> ll_init(); >> #endif >> +#ifdef CONFIG_BT_HCIUART_ATH >> + ath_init(); >> +#endif >> >> return 0; >> } >> @@ -559,6 +562,9 @@ static void __exit hci_uart_exit(void) >> #ifdef CONFIG_BT_HCIUART_LL >> ll_deinit(); >> #endif >> +#ifdef CONFIG_BT_HCIUART_ATH >> + ath_deinit(); >> +#endif >> >> /* Release tty registration of line discipline */ >> if ((err = tty_unregister_ldisc(N_HCI))) >> diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h >> index 50113db..385537f 100644 >> --- a/drivers/bluetooth/hci_uart.h >> +++ b/drivers/bluetooth/hci_uart.h >> @@ -33,13 +33,14 @@ >> #define HCIUARTGETDEVICE _IOR('U', 202, int) >> >> /* UART protocols */ >> -#define HCI_UART_MAX_PROTO 5 >> +#define HCI_UART_MAX_PROTO 6 >> >> #define HCI_UART_H4 0 >> #define HCI_UART_BCSP 1 >> #define HCI_UART_3WIRE 2 >> #define HCI_UART_H4DS 3 >> #define HCI_UART_LL 4 >> +#define HCI_UART_ATH 5 >> >> struct hci_uart; >> >> @@ -91,3 +92,8 @@ int bcsp_deinit(void); >> int ll_init(void); >> int ll_deinit(void); >> #endif >> + >> +#ifdef CONFIG_BT_HCIUART_ATH >> +int ath_init(void); >> +int ath_deinit(void); >> +#endif >> -- >> 1.7.0 >> >> >> >> >> > > -- > Gustavo F. Padovan > http://padovan.org ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v4] Add support for the Atheros AR300x Bluetooth Chip 2010-04-22 6:54 ` Suraj Sumangala @ 2010-04-22 8:59 ` Gustavo F. Padovan 0 siblings, 0 replies; 36+ messages in thread From: Gustavo F. Padovan @ 2010-04-22 8:59 UTC (permalink / raw) To: Suraj Sumangala Cc: Suraj Sumangala, linux-bluetooth@vger.kernel.org, marcel@holtmann.org, Luis Rodriguez, Jothikumar Mothilal Hi Suraj, * Suraj Sumangala <suraj@atheros.com> [2010-04-22 12:24:04 +0530]: > > Hi Gustavo, > > Gustavo F. Padovan wrote: > >* suraj <suraj@atheros.com> [2010-04-21 15:52:17 +0530]: > > > >>This implements the Atheros Bluetooth serial protocol to > >>support the AR300x Bluetooth chipsets. > >>The serial protocol implements enhanced power management > >>features for the AR300x chipsets. > >> > >>Reviewed-by: Luis R. Rodriguez <lrodriguez@atheros.com> > >>Signed-off-by: Suraj <suraj@atheros.com> > >> > >>--- > >> drivers/bluetooth/Kconfig | 14 ++ > >> drivers/bluetooth/Makefile | 1 + > >> drivers/bluetooth/hci_ath.c | 378 +++++++++++++++++++++++++++++++++++++++++ > >> drivers/bluetooth/hci_ldisc.c | 6 + > >> drivers/bluetooth/hci_uart.h | 8 +- > >> 5 files changed, 406 insertions(+), 1 deletions(-) > >> create mode 100755 drivers/bluetooth/hci_ath.c > >> ..snip.. > >>+ > >>+static void ath_check_data_len(struct ath_struct *ath, int len) > >>+{ > >>+ int room = skb_tailroom(ath->rx_skb); > >>+ > >>+ BT_DBG("len %d room %d", len, room); > >>+ > >>+ if (len > room) { > >>+ BT_ERR("Data length is too large"); > >>+ kfree_skb(ath->rx_skb); > >>+ ath->rx_state = HCIATH_W4_PACKET_TYPE; > >>+ ath->rx_skb = NULL; > >>+ ath->rx_count = 0; > >>+ } else { > >>+ ath->rx_state = HCIATH_W4_DATA; > >>+ ath->rx_count = len; > >>+ } > >>+} > >>+ > >>+/* Recv data */ > >>+static int ath_recv(struct hci_uart *hu, void *data, int count) > >>+{ > >>+ struct ath_struct *ath = hu->priv; > >>+ char *ptr = data; > >>+ struct hci_event_hdr *eh; > >>+ struct hci_acl_hdr *ah; > >>+ struct hci_sco_hdr *sh; > >>+ int len, type, dlen; > >>+ > >>+ BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, > >>+ ath->rx_state, ath->rx_count); > >>+ > >>+ while (count) { > >>+ if (ath->rx_count) { > >>+ > >>+ len = min_t(unsigned int, ath->rx_count, count); > >>+ memcpy(skb_put(ath->rx_skb, len), ptr, len); > >>+ ath->rx_count -= len; > >>+ count -= len; > >>+ ptr += len; > >>+ > >>+ if (ath->rx_count) > >>+ continue; > >>+ switch (ath->rx_state) { > >>+ case HCIATH_W4_DATA: > >>+ hci_recv_frame(ath->rx_skb); > >>+ ath->rx_state = HCIATH_W4_PACKET_TYPE; > >>+ ath->rx_skb = NULL; > >>+ ath->rx_count = 0; > >>+ continue; > >>+ > >>+ case HCIATH_W4_EVENT_HDR: > >>+ eh = (struct hci_event_hdr *)ath->rx_skb->data; > > > >Use hci_event_hdr() here like hci_h4.c does. > > > >>+ > >>+ BT_DBG("Event header: evt 0x%2.2x plen %d", > >>+ eh->evt, eh->plen); > >>+ > >>+ ath_check_data_len(ath, eh->plen); > >>+ continue; > >>+ > >>+ case HCIATH_W4_ACL_HDR: > >>+ ah = (struct hci_acl_hdr *)ath->rx_skb->data; > > > >And hci_acl_hdr() here. > > > >>+ dlen = __le16_to_cpu(ah->dlen); > >>+ > >>+ BT_DBG("ACL header: dlen %d", dlen); > >>+ > >>+ ath_check_data_len(ath, dlen); > >>+ continue; > >>+ > >>+ case HCIATH_W4_SCO_HDR: > >>+ sh = (struct hci_sco_hdr *)ath->rx_skb->data; > > > >hci_sco_hdr() here. > > > >>+ > >>+ BT_DBG("SCO header: dlen %d", sh->dlen); > >>+ > >>+ ath_check_data_len(ath, sh->dlen); > >>+ continue; > >>+ > >>+ } > >>+ } > >>+ > >>+ /* HCIATH_W4_PACKET_TYPE */ > >>+ switch (*ptr) { > >>+ case HCI_EVENT_PKT: > >>+ BT_DBG("Event packet"); > >>+ ath->rx_state = HCIATH_W4_EVENT_HDR; > >>+ ath->rx_count = HCI_EVENT_HDR_SIZE; > >>+ type = HCI_EVENT_PKT; > >>+ break; > >>+ > >>+ case HCI_ACLDATA_PKT: > >>+ BT_DBG("ACL packet"); > >>+ ath->rx_state = HCIATH_W4_ACL_HDR; > >>+ ath->rx_count = HCI_ACL_HDR_SIZE; > >>+ type = HCI_ACLDATA_PKT; > >>+ break; > >>+ > >>+ case HCI_SCODATA_PKT: > >>+ BT_DBG("SCO packet"); > >>+ ath->rx_state = HCIATH_W4_SCO_HDR; > >>+ ath->rx_count = HCI_SCO_HDR_SIZE; > >>+ type = HCI_SCODATA_PKT; > >>+ break; > >>+ > >>+ default: > >>+ BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); > >>+ hu->hdev->stat.err_rx++; > >>+ ptr++; > >>+ count--; > >>+ continue; > >>+ > >>+ }; > >>+ ptr++; > >>+ count--; > >>+ > >>+ /* Allocate packet */ > >>+ ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); > >>+ if (!ath->rx_skb) { > >>+ BT_ERR("Can't allocate mem for new packet"); > >>+ ath->rx_state = HCIATH_W4_PACKET_TYPE; > >>+ ath->rx_count = 0; > >>+ > >>+ return -ENOMEM; > >>+ } > >>+ ath->rx_skb->dev = (void *)hu->hdev; > >>+ bt_cb(ath->rx_skb)->pkt_type = type; > >>+ } > >>+ > >>+ return count; > >>+} > > > >Just out of curiosity. I never worked in the driver world, but is it ok > >to duplicate lots of code when working with drivers? hci_h4.c and this > >patch share lots of similar code. ath_recv() and h4_recv() are exactly > >the same except for one line, the ath->rx_count = 0; at case HCIATH_W4_DATA. > >Does this line makes real difference? recv() is not the only function > >duplicating code here. > > Let me try to anwer you here. I am also new to the driver world, so > correct me if I am wrong. > > Both drivers do the same job, expect that the ATH driver does > something more on the data transmit path. So, the receive path does > the same thing. That is the reason why both looks same. This could > possibly change later as new feature will be added to the ATH > protocol. Ok. I'm thinking if it is not possible create a separated 'lib' with the common code. That's why I asked about the code duplication. I don't know how worth it is, since we have only 3 drivers using similar code. > > > The function "hci_recv_fragment()" could potentially replace most of > the code in the ath_recv() function. But, unfortunately this > function require the caller to provide the HCI Packet type as > parameter. > > This defeats all the advantage of "hci_recv_fragment()" in a UART > HCI transport driver as all types of packets are received through > the same callback. So the caller will have to write the same messy > code in ath_recv() to find out the packet type negating all the > advantages of "hci_recv_fragment()". > > > > > >>+ > >>+static struct hci_uart_proto athp = { > >>+ .id = HCI_UART_ATH, > >>+ .open = ath_open, > >>+ .close = ath_close, > >>+ .recv = ath_recv, > >>+ .enqueue = ath_enqueue, > >>+ .dequeue = ath_dequeue, > >>+ .flush = ath_flush, > >>+}; > >>+ > >>+int ath_init(void) > >>+{ > >>+ int err = hci_uart_register_proto(&athp); > >>+ > >>+ if (!err) > >>+ BT_INFO("HCIATH protocol initialized"); > >>+ else > >>+ BT_ERR("HCIATH protocol registration failed"); > >>+ > >>+ return err; > >>+} > > > >BTW, we never check this return value on hci_ldisc.c, why? > > you, are correct. Just thought of keeping the same signature used by > other protocol. moreover hci_ldisc.c being a common file used by all > protocol it is possible that somebody want to check the return value > at later point of time. So, kept it that way so that we do not have > a problem then :-D > > Your thoughts? Yes, your code is right, the return value should be kept. The problem is that hci_uart_init() doesn't check any other protocol initialization (h4, bcsp and ll). My thought is: we really need to check? Marcel, can you answer that? > > > >>+ > >>+int ath_deinit(void) > >>+{ > >>+ return hci_uart_unregister_proto(&athp); > >>+} > >>diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c > >>index 76a1abb..7dd76d1 100644 > >>--- a/drivers/bluetooth/hci_ldisc.c > >>+++ b/drivers/bluetooth/hci_ldisc.c > >>@@ -542,6 +542,9 @@ static int __init hci_uart_init(void) > >> #ifdef CONFIG_BT_HCIUART_LL > >> ll_init(); > >> #endif > >>+#ifdef CONFIG_BT_HCIUART_ATH > >>+ ath_init(); > >>+#endif > >> > >> return 0; > >> } > >>@@ -559,6 +562,9 @@ static void __exit hci_uart_exit(void) > >> #ifdef CONFIG_BT_HCIUART_LL > >> ll_deinit(); > >> #endif > >>+#ifdef CONFIG_BT_HCIUART_ATH > >>+ ath_deinit(); > >>+#endif > >> > >> /* Release tty registration of line discipline */ > >> if ((err = tty_unregister_ldisc(N_HCI))) > >>diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h > >>index 50113db..385537f 100644 > >>--- a/drivers/bluetooth/hci_uart.h > >>+++ b/drivers/bluetooth/hci_uart.h > >>@@ -33,13 +33,14 @@ > >> #define HCIUARTGETDEVICE _IOR('U', 202, int) > >> > >> /* UART protocols */ > >>-#define HCI_UART_MAX_PROTO 5 > >>+#define HCI_UART_MAX_PROTO 6 > >> > >> #define HCI_UART_H4 0 > >> #define HCI_UART_BCSP 1 > >> #define HCI_UART_3WIRE 2 > >> #define HCI_UART_H4DS 3 > >> #define HCI_UART_LL 4 > >>+#define HCI_UART_ATH 5 > >> > >> struct hci_uart; > >> > >>@@ -91,3 +92,8 @@ int bcsp_deinit(void); > >> int ll_init(void); > >> int ll_deinit(void); > >> #endif > >>+ > >>+#ifdef CONFIG_BT_HCIUART_ATH > >>+int ath_init(void); > >>+int ath_deinit(void); > >>+#endif > >>-- > >>1.7.0 > >> > >> > >> > >> > >> > > > >-- > >Gustavo F. Padovan > >http://padovan.org > > -- > To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Gustavo F. Padovan http://padovan.org ^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip 2010-04-21 10:22 ` [PATCH v4] Add support for the " suraj 2010-04-21 17:30 ` Luis R. Rodriguez 2010-04-22 6:10 ` Gustavo F. Padovan @ 2010-04-22 9:10 ` suraj 2010-04-26 11:00 ` suraj ` (2 more replies) 2 siblings, 3 replies; 36+ messages in thread From: suraj @ 2010-04-22 9:10 UTC (permalink / raw) To: linux-bluetooth; +Cc: marcel, Luis.Rodriguez, Jothikumar.Mothilal, gfpadovan This implements the Atheros Bluetooth serial protocol to support the AR300x Bluetooth chipsets. The serial protocol implements enhanced power management features for the AR300x chipsets. Reviewed-by: Luis R. Rodriguez <lrodriguez@atheros.com> Reviewed-by: Gustavo F. Padovan <gustavo@padovan.org> Signed-off-by: Suraj <suraj@atheros.com> --- drivers/bluetooth/Kconfig | 14 ++ drivers/bluetooth/Makefile | 1 + drivers/bluetooth/hci_ath.c | 378 +++++++++++++++++++++++++++++++++++++++++ drivers/bluetooth/hci_ldisc.c | 6 + drivers/bluetooth/hci_uart.h | 8 +- 5 files changed, 406 insertions(+), 1 deletions(-) create mode 100755 drivers/bluetooth/hci_ath.c diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 058fbcc..5546142 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -58,6 +58,20 @@ config BT_HCIUART_BCSP Say Y here to compile support for HCI BCSP protocol. +config BT_HCIUART_ATH + bool "Atheros AR300x serial Bluetooth support" + depends on BT_HCIUART + help + HCIATH (HCI Atheros) is a serial protocol for communication + between the host and Atheros AR300x Bluetooth devices. The + protocol implements enhaned power management features for the + the AR300x chipsets. it lets the controller chip go to sleep + mode if there is no Bluetooth activity for some time and wakes + up the chip in case of a Bluetooth activity. Enable this option + if you have an UART Atheros AR300x serial device. + + Say Y here to compile support for HCIATH protocol. + config BT_HCIUART_LL bool "HCILL protocol support" depends on BT_HCIUART diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 7e5aed5..1481faa 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -26,4 +26,5 @@ hci_uart-y := hci_ldisc.o hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o +hci_uart-$(CONFIG_BT_HCIUART_ATH) += hci_ath.o hci_uart-objs := $(hci_uart-y) diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c new file mode 100755 index 0000000..152a1f6 --- /dev/null +++ b/drivers/bluetooth/hci_ath.c @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2009-2010 Atheros Communications Inc. + * + * 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 <linux/module.h> +#include <linux/kernel.h> + +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/errno.h> +#include <linux/ioctl.h> +#include <linux/skbuff.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +#include "hci_uart.h" + +/* HCIATH receiver States */ +#define HCIATH_W4_PACKET_TYPE 0 +#define HCIATH_W4_EVENT_HDR 1 +#define HCIATH_W4_ACL_HDR 2 +#define HCIATH_W4_SCO_HDR 3 +#define HCIATH_W4_DATA 4 + +struct ath_struct { + struct hci_uart *hu; + unsigned int rx_state; + unsigned int rx_count; + unsigned int cur_sleep; + + spinlock_t hciath_lock; + struct sk_buff *rx_skb; + struct sk_buff_head txq; + wait_queue_head_t wqevt; + struct work_struct ctxtsw; +}; + +static int ath_wakeup_ar3001(struct tty_struct *tty) +{ + struct termios settings; + int status = tty->driver->ops->tiocmget(tty, NULL); + + if (status & TIOCM_CTS) + return status; + + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); + + /* Disable Automatic RTSCTS */ + settings.c_cflag &= ~CRTSCTS; + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); + + status = tty->driver->ops->tiocmget(tty, NULL); + + /* Clear RTS first */ + tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS); + mdelay(20); + + status = tty->driver->ops->tiocmget(tty, NULL); + + /* Set RTS, wake up board */ + tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00); + mdelay(20); + + status = tty->driver->ops->tiocmget(tty, NULL); + + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); + + settings.c_cflag |= CRTSCTS; + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); + + return status; +} + +static void ath_hci_uart_work(struct work_struct *work) +{ + int status; + struct ath_struct *ath; + struct hci_uart *hu; + struct tty_struct *tty; + + ath = container_of(work, struct ath_struct, ctxtsw); + + hu = ath->hu; + tty = hu->tty; + + /* verify and wake up controller */ + if (ath->cur_sleep) { + status = ath_wakeup_ar3001(tty); + + if (!(status & TIOCM_CTS)) + return; + } + + /* Ready to send Data */ + clear_bit(HCI_UART_SENDING, &hu->tx_state); + hci_uart_tx_wakeup(hu); +} + +/* Initialize protocol */ +static int ath_open(struct hci_uart *hu) +{ + struct ath_struct *ath; + + BT_DBG("hu %p", hu); + + ath = kzalloc(sizeof(*ath), GFP_ATOMIC); + if (!ath) + return -ENOMEM; + + skb_queue_head_init(&ath->txq); + spin_lock_init(&ath->hciath_lock); + + hu->priv = ath; + ath->hu = hu; + + init_waitqueue_head(&ath->wqevt); + INIT_WORK(&ath->ctxtsw, ath_hci_uart_work); + + return 0; +} + +/* Flush protocol data */ +static int ath_flush(struct hci_uart *hu) +{ + struct ath_struct *ath = hu->priv; + + BT_DBG("hu %p", hu); + + skb_queue_purge(&ath->txq); + + return 0; +} + +/* Close protocol */ +static int ath_close(struct hci_uart *hu) +{ + struct ath_struct *ath = hu->priv; + + BT_DBG("hu %p", hu); + + skb_queue_purge(&ath->txq); + + kfree_skb(ath->rx_skb); + + cancel_work_sync(&ath->ctxtsw); + + hu->priv = NULL; + kfree(ath); + + return 0; +} + +/* Enqueue frame for transmittion */ +static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) +{ + struct ath_struct *ath = hu->priv; + + if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { + + /* Discard SCO packet. AR300x does not support SCO over HCI */ + BT_DBG("SCO Packet over HCI received Dropping"); + + kfree(skb); + + return 0; + } + + BT_DBG("hu %p skb %p", hu, skb); + + /* Prepend skb with frame type */ + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + + skb_queue_tail(&ath->txq, skb); + set_bit(HCI_UART_SENDING, &hu->tx_state); + + schedule_work(&ath->ctxtsw); + + return 0; +} + +static struct sk_buff *ath_dequeue(struct hci_uart *hu) +{ + struct ath_struct *ath = hu->priv; + struct sk_buff *skbuf; + + skbuf = skb_dequeue(&ath->txq); + + if (!skbuf) + return NULL; + + /* + * Check if the HCI command is HCI sleep enable and + * update the sleep enable flag with command parameter. + * + * Value of sleep enable flag will be used later + * to verify if controller has to be woken up before + * sending any packet. + */ + if (skbuf->data[0] == 0x01 && + skbuf->data[1] == 0x04 && + skbuf->data[2] == 0xFC) + ath->cur_sleep = skbuf->data[4]; + + return skbuf; +} + +static void ath_check_data_len(struct ath_struct *ath, int len) +{ + int room = skb_tailroom(ath->rx_skb); + + BT_DBG("len %d room %d", len, room); + + if (len > room) { + BT_ERR("Data length is too large"); + kfree_skb(ath->rx_skb); + ath->rx_state = HCIATH_W4_PACKET_TYPE; + ath->rx_skb = NULL; + ath->rx_count = 0; + } else { + ath->rx_state = HCIATH_W4_DATA; + ath->rx_count = len; + } +} + +/* Recv data */ +static int ath_recv(struct hci_uart *hu, void *data, int count) +{ + struct ath_struct *ath = hu->priv; + char *ptr = data; + struct hci_event_hdr *eh; + struct hci_acl_hdr *ah; + struct hci_sco_hdr *sh; + int len, type, dlen; + + BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, + ath->rx_state, ath->rx_count); + + while (count) { + if (ath->rx_count) { + + len = min_t(unsigned int, ath->rx_count, count); + memcpy(skb_put(ath->rx_skb, len), ptr, len); + ath->rx_count -= len; + count -= len; + ptr += len; + + if (ath->rx_count) + continue; + switch (ath->rx_state) { + case HCIATH_W4_DATA: + hci_recv_frame(ath->rx_skb); + ath->rx_state = HCIATH_W4_PACKET_TYPE; + ath->rx_skb = NULL; + ath->rx_count = 0; + continue; + + case HCIATH_W4_EVENT_HDR: + eh = hci_event_hdr(ath->rx_skb); + + BT_DBG("Event header: evt 0x%2.2x plen %d", + eh->evt, eh->plen); + + ath_check_data_len(ath, eh->plen); + continue; + + case HCIATH_W4_ACL_HDR: + ah = hci_acl_hdr(ath->rx_skb); + dlen = __le16_to_cpu(ah->dlen); + + BT_DBG("ACL header: dlen %d", dlen); + + ath_check_data_len(ath, dlen); + continue; + + case HCIATH_W4_SCO_HDR: + sh = hci_sco_hdr(ath->rx_skb); + + BT_DBG("SCO header: dlen %d", sh->dlen); + + ath_check_data_len(ath, sh->dlen); + continue; + + } + } + + /* HCIATH_W4_PACKET_TYPE */ + switch (*ptr) { + case HCI_EVENT_PKT: + BT_DBG("Event packet"); + ath->rx_state = HCIATH_W4_EVENT_HDR; + ath->rx_count = HCI_EVENT_HDR_SIZE; + type = HCI_EVENT_PKT; + break; + + case HCI_ACLDATA_PKT: + BT_DBG("ACL packet"); + ath->rx_state = HCIATH_W4_ACL_HDR; + ath->rx_count = HCI_ACL_HDR_SIZE; + type = HCI_ACLDATA_PKT; + break; + + case HCI_SCODATA_PKT: + BT_DBG("SCO packet"); + ath->rx_state = HCIATH_W4_SCO_HDR; + ath->rx_count = HCI_SCO_HDR_SIZE; + type = HCI_SCODATA_PKT; + break; + + default: + BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); + hu->hdev->stat.err_rx++; + ptr++; + count--; + continue; + + }; + ptr++; + count--; + + /* Allocate packet */ + ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + if (!ath->rx_skb) { + BT_ERR("Can't allocate mem for new packet"); + ath->rx_state = HCIATH_W4_PACKET_TYPE; + ath->rx_count = 0; + + return -ENOMEM; + } + ath->rx_skb->dev = (void *)hu->hdev; + bt_cb(ath->rx_skb)->pkt_type = type; + } + + return count; +} + +static struct hci_uart_proto athp = { + .id = HCI_UART_ATH, + .open = ath_open, + .close = ath_close, + .recv = ath_recv, + .enqueue = ath_enqueue, + .dequeue = ath_dequeue, + .flush = ath_flush, +}; + +int ath_init(void) +{ + int err = hci_uart_register_proto(&athp); + + if (!err) + BT_INFO("HCIATH protocol initialized"); + else + BT_ERR("HCIATH protocol registration failed"); + + return err; +} + +int ath_deinit(void) +{ + return hci_uart_unregister_proto(&athp); +} diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 76a1abb..7dd76d1 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -542,6 +542,9 @@ static int __init hci_uart_init(void) #ifdef CONFIG_BT_HCIUART_LL ll_init(); #endif +#ifdef CONFIG_BT_HCIUART_ATH + ath_init(); +#endif return 0; } @@ -559,6 +562,9 @@ static void __exit hci_uart_exit(void) #ifdef CONFIG_BT_HCIUART_LL ll_deinit(); #endif +#ifdef CONFIG_BT_HCIUART_ATH + ath_deinit(); +#endif /* Release tty registration of line discipline */ if ((err = tty_unregister_ldisc(N_HCI))) diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 50113db..385537f 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -33,13 +33,14 @@ #define HCIUARTGETDEVICE _IOR('U', 202, int) /* UART protocols */ -#define HCI_UART_MAX_PROTO 5 +#define HCI_UART_MAX_PROTO 6 #define HCI_UART_H4 0 #define HCI_UART_BCSP 1 #define HCI_UART_3WIRE 2 #define HCI_UART_H4DS 3 #define HCI_UART_LL 4 +#define HCI_UART_ATH 5 struct hci_uart; @@ -91,3 +92,8 @@ int bcsp_deinit(void); int ll_init(void); int ll_deinit(void); #endif + +#ifdef CONFIG_BT_HCIUART_ATH +int ath_init(void); +int ath_deinit(void); +#endif -- 1.7.0 ^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip 2010-04-22 9:10 ` [PATCH v5] " suraj @ 2010-04-26 11:00 ` suraj 2010-04-27 6:19 ` [PATCH] New Firmware for Atheros bluetooth chipset AR3011 suraj 2010-05-05 12:33 ` [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip suraj 2010-05-12 13:47 ` [PATCH v3] hciattach application support for " suraj 2010-05-20 16:09 ` [PATCH v5] Add support for the " Marcel Holtmann 2 siblings, 2 replies; 36+ messages in thread From: suraj @ 2010-04-26 11:00 UTC (permalink / raw) To: linux-bluetooth; +Cc: marcel, Luis.Rodriguez, Jothikumar.Mothilal, gfpadovan Hi marcel, On Thu, 2010-04-22 at 14:40 +0530, suraj wrote: > This implements the Atheros Bluetooth serial protocol to > support the AR300x Bluetooth chipsets. > The serial protocol implements enhanced power management > features for the AR300x chipsets. > > Reviewed-by: Luis R. Rodriguez <lrodriguez@atheros.com> > Reviewed-by: Gustavo F. Padovan <gustavo@padovan.org> > Signed-off-by: Suraj <suraj@atheros.com> > > --- > drivers/bluetooth/Kconfig | 14 ++ > drivers/bluetooth/Makefile | 1 + > drivers/bluetooth/hci_ath.c | 378 +++++++++++++++++++++++++++++++++++++++++ > drivers/bluetooth/hci_ldisc.c | 6 + > drivers/bluetooth/hci_uart.h | 8 +- > 5 files changed, 406 insertions(+), 1 deletions(-) > create mode 100755 drivers/bluetooth/hci_ath.c > > diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig > index 058fbcc..5546142 100644 > --- a/drivers/bluetooth/Kconfig > +++ b/drivers/bluetooth/Kconfig > @@ -58,6 +58,20 @@ config BT_HCIUART_BCSP > > Say Y here to compile support for HCI BCSP protocol. > > +config BT_HCIUART_ATH > + bool "Atheros AR300x serial Bluetooth support" > + depends on BT_HCIUART > + help > + HCIATH (HCI Atheros) is a serial protocol for communication > + between the host and Atheros AR300x Bluetooth devices. The > + protocol implements enhaned power management features for the > + the AR300x chipsets. it lets the controller chip go to sleep > + mode if there is no Bluetooth activity for some time and wakes > + up the chip in case of a Bluetooth activity. Enable this option > + if you have an UART Atheros AR300x serial device. > + > + Say Y here to compile support for HCIATH protocol. > + > config BT_HCIUART_LL > bool "HCILL protocol support" > depends on BT_HCIUART > diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile > index 7e5aed5..1481faa 100644 > --- a/drivers/bluetooth/Makefile > +++ b/drivers/bluetooth/Makefile > @@ -26,4 +26,5 @@ hci_uart-y := hci_ldisc.o > hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o > hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o > hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o > +hci_uart-$(CONFIG_BT_HCIUART_ATH) += hci_ath.o > hci_uart-objs := $(hci_uart-y) > diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c > new file mode 100755 > index 0000000..152a1f6 > --- /dev/null > +++ b/drivers/bluetooth/hci_ath.c > @@ -0,0 +1,378 @@ > +/* > + * Copyright (c) 2009-2010 Atheros Communications Inc. > + * > + * 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 <linux/module.h> > +#include <linux/kernel.h> > + > +#include <linux/init.h> > +#include <linux/slab.h> > +#include <linux/tty.h> > +#include <linux/errno.h> > +#include <linux/ioctl.h> > +#include <linux/skbuff.h> > + > +#include <net/bluetooth/bluetooth.h> > +#include <net/bluetooth/hci_core.h> > + > +#include "hci_uart.h" > + > +/* HCIATH receiver States */ > +#define HCIATH_W4_PACKET_TYPE 0 > +#define HCIATH_W4_EVENT_HDR 1 > +#define HCIATH_W4_ACL_HDR 2 > +#define HCIATH_W4_SCO_HDR 3 > +#define HCIATH_W4_DATA 4 > + > +struct ath_struct { > + struct hci_uart *hu; > + unsigned int rx_state; > + unsigned int rx_count; > + unsigned int cur_sleep; > + > + spinlock_t hciath_lock; > + struct sk_buff *rx_skb; > + struct sk_buff_head txq; > + wait_queue_head_t wqevt; > + struct work_struct ctxtsw; > +}; > + > +static int ath_wakeup_ar3001(struct tty_struct *tty) > +{ > + struct termios settings; > + int status = tty->driver->ops->tiocmget(tty, NULL); > + > + if (status & TIOCM_CTS) > + return status; > + > + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); > + > + /* Disable Automatic RTSCTS */ > + settings.c_cflag &= ~CRTSCTS; > + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + /* Clear RTS first */ > + tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS); > + mdelay(20); > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + /* Set RTS, wake up board */ > + tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00); > + mdelay(20); > + > + status = tty->driver->ops->tiocmget(tty, NULL); > + > + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); > + > + settings.c_cflag |= CRTSCTS; > + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); > + > + return status; > +} > + > +static void ath_hci_uart_work(struct work_struct *work) > +{ > + int status; > + struct ath_struct *ath; > + struct hci_uart *hu; > + struct tty_struct *tty; > + > + ath = container_of(work, struct ath_struct, ctxtsw); > + > + hu = ath->hu; > + tty = hu->tty; > + > + /* verify and wake up controller */ > + if (ath->cur_sleep) { > + status = ath_wakeup_ar3001(tty); > + > + if (!(status & TIOCM_CTS)) > + return; > + } > + > + /* Ready to send Data */ > + clear_bit(HCI_UART_SENDING, &hu->tx_state); > + hci_uart_tx_wakeup(hu); > +} > + > +/* Initialize protocol */ > +static int ath_open(struct hci_uart *hu) > +{ > + struct ath_struct *ath; > + > + BT_DBG("hu %p", hu); > + > + ath = kzalloc(sizeof(*ath), GFP_ATOMIC); > + if (!ath) > + return -ENOMEM; > + > + skb_queue_head_init(&ath->txq); > + spin_lock_init(&ath->hciath_lock); > + > + hu->priv = ath; > + ath->hu = hu; > + > + init_waitqueue_head(&ath->wqevt); > + INIT_WORK(&ath->ctxtsw, ath_hci_uart_work); > + > + return 0; > +} > + > +/* Flush protocol data */ > +static int ath_flush(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + > + BT_DBG("hu %p", hu); > + > + skb_queue_purge(&ath->txq); > + > + return 0; > +} > + > +/* Close protocol */ > +static int ath_close(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + > + BT_DBG("hu %p", hu); > + > + skb_queue_purge(&ath->txq); > + > + kfree_skb(ath->rx_skb); > + > + cancel_work_sync(&ath->ctxtsw); > + > + hu->priv = NULL; > + kfree(ath); > + > + return 0; > +} > + > +/* Enqueue frame for transmittion */ > +static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) > +{ > + struct ath_struct *ath = hu->priv; > + > + if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { > + > + /* Discard SCO packet. AR300x does not support SCO over HCI */ > + BT_DBG("SCO Packet over HCI received Dropping"); > + > + kfree(skb); > + > + return 0; > + } > + > + BT_DBG("hu %p skb %p", hu, skb); > + > + /* Prepend skb with frame type */ > + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); > + > + skb_queue_tail(&ath->txq, skb); > + set_bit(HCI_UART_SENDING, &hu->tx_state); > + > + schedule_work(&ath->ctxtsw); > + > + return 0; > +} > + > +static struct sk_buff *ath_dequeue(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + struct sk_buff *skbuf; > + > + skbuf = skb_dequeue(&ath->txq); > + > + if (!skbuf) > + return NULL; > + > + /* > + * Check if the HCI command is HCI sleep enable and > + * update the sleep enable flag with command parameter. > + * > + * Value of sleep enable flag will be used later > + * to verify if controller has to be woken up before > + * sending any packet. > + */ > + if (skbuf->data[0] == 0x01 && > + skbuf->data[1] == 0x04 && > + skbuf->data[2] == 0xFC) > + ath->cur_sleep = skbuf->data[4]; > + > + return skbuf; > +} > + > +static void ath_check_data_len(struct ath_struct *ath, int len) > +{ > + int room = skb_tailroom(ath->rx_skb); > + > + BT_DBG("len %d room %d", len, room); > + > + if (len > room) { > + BT_ERR("Data length is too large"); > + kfree_skb(ath->rx_skb); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_skb = NULL; > + ath->rx_count = 0; > + } else { > + ath->rx_state = HCIATH_W4_DATA; > + ath->rx_count = len; > + } > +} > + > +/* Recv data */ > +static int ath_recv(struct hci_uart *hu, void *data, int count) > +{ > + struct ath_struct *ath = hu->priv; > + char *ptr = data; > + struct hci_event_hdr *eh; > + struct hci_acl_hdr *ah; > + struct hci_sco_hdr *sh; > + int len, type, dlen; > + > + BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, > + ath->rx_state, ath->rx_count); > + > + while (count) { > + if (ath->rx_count) { > + > + len = min_t(unsigned int, ath->rx_count, count); > + memcpy(skb_put(ath->rx_skb, len), ptr, len); > + ath->rx_count -= len; > + count -= len; > + ptr += len; > + > + if (ath->rx_count) > + continue; > + switch (ath->rx_state) { > + case HCIATH_W4_DATA: > + hci_recv_frame(ath->rx_skb); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_skb = NULL; > + ath->rx_count = 0; > + continue; > + > + case HCIATH_W4_EVENT_HDR: > + eh = hci_event_hdr(ath->rx_skb); > + > + BT_DBG("Event header: evt 0x%2.2x plen %d", > + eh->evt, eh->plen); > + > + ath_check_data_len(ath, eh->plen); > + continue; > + > + case HCIATH_W4_ACL_HDR: > + ah = hci_acl_hdr(ath->rx_skb); > + dlen = __le16_to_cpu(ah->dlen); > + > + BT_DBG("ACL header: dlen %d", dlen); > + > + ath_check_data_len(ath, dlen); > + continue; > + > + case HCIATH_W4_SCO_HDR: > + sh = hci_sco_hdr(ath->rx_skb); > + > + BT_DBG("SCO header: dlen %d", sh->dlen); > + > + ath_check_data_len(ath, sh->dlen); > + continue; > + > + } > + } > + > + /* HCIATH_W4_PACKET_TYPE */ > + switch (*ptr) { > + case HCI_EVENT_PKT: > + BT_DBG("Event packet"); > + ath->rx_state = HCIATH_W4_EVENT_HDR; > + ath->rx_count = HCI_EVENT_HDR_SIZE; > + type = HCI_EVENT_PKT; > + break; > + > + case HCI_ACLDATA_PKT: > + BT_DBG("ACL packet"); > + ath->rx_state = HCIATH_W4_ACL_HDR; > + ath->rx_count = HCI_ACL_HDR_SIZE; > + type = HCI_ACLDATA_PKT; > + break; > + > + case HCI_SCODATA_PKT: > + BT_DBG("SCO packet"); > + ath->rx_state = HCIATH_W4_SCO_HDR; > + ath->rx_count = HCI_SCO_HDR_SIZE; > + type = HCI_SCODATA_PKT; > + break; > + > + default: > + BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); > + hu->hdev->stat.err_rx++; > + ptr++; > + count--; > + continue; > + > + }; > + ptr++; > + count--; > + > + /* Allocate packet */ > + ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); > + if (!ath->rx_skb) { > + BT_ERR("Can't allocate mem for new packet"); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_count = 0; > + > + return -ENOMEM; > + } > + ath->rx_skb->dev = (void *)hu->hdev; > + bt_cb(ath->rx_skb)->pkt_type = type; > + } > + > + return count; > +} > + > +static struct hci_uart_proto athp = { > + .id = HCI_UART_ATH, > + .open = ath_open, > + .close = ath_close, > + .recv = ath_recv, > + .enqueue = ath_enqueue, > + .dequeue = ath_dequeue, > + .flush = ath_flush, > +}; > + > +int ath_init(void) > +{ > + int err = hci_uart_register_proto(&athp); > + > + if (!err) > + BT_INFO("HCIATH protocol initialized"); > + else > + BT_ERR("HCIATH protocol registration failed"); > + > + return err; > +} > + > +int ath_deinit(void) > +{ > + return hci_uart_unregister_proto(&athp); > +} > diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c > index 76a1abb..7dd76d1 100644 > --- a/drivers/bluetooth/hci_ldisc.c > +++ b/drivers/bluetooth/hci_ldisc.c > @@ -542,6 +542,9 @@ static int __init hci_uart_init(void) > #ifdef CONFIG_BT_HCIUART_LL > ll_init(); > #endif > +#ifdef CONFIG_BT_HCIUART_ATH > + ath_init(); > +#endif > > return 0; > } > @@ -559,6 +562,9 @@ static void __exit hci_uart_exit(void) > #ifdef CONFIG_BT_HCIUART_LL > ll_deinit(); > #endif > +#ifdef CONFIG_BT_HCIUART_ATH > + ath_deinit(); > +#endif > > /* Release tty registration of line discipline */ > if ((err = tty_unregister_ldisc(N_HCI))) > diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h > index 50113db..385537f 100644 > --- a/drivers/bluetooth/hci_uart.h > +++ b/drivers/bluetooth/hci_uart.h > @@ -33,13 +33,14 @@ > #define HCIUARTGETDEVICE _IOR('U', 202, int) > > /* UART protocols */ > -#define HCI_UART_MAX_PROTO 5 > +#define HCI_UART_MAX_PROTO 6 > > #define HCI_UART_H4 0 > #define HCI_UART_BCSP 1 > #define HCI_UART_3WIRE 2 > #define HCI_UART_H4DS 3 > #define HCI_UART_LL 4 > +#define HCI_UART_ATH 5 > > struct hci_uart; > > @@ -91,3 +92,8 @@ int bcsp_deinit(void); > int ll_init(void); > int ll_deinit(void); > #endif > + > +#ifdef CONFIG_BT_HCIUART_ATH > +int ath_init(void); > +int ath_deinit(void); > +#endif Can you verify the patch let me know your comments? Regards Suraj ^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH] New Firmware for Atheros bluetooth chipset AR3011 2010-04-26 11:00 ` suraj @ 2010-04-27 6:19 ` suraj 2010-04-27 8:28 ` [PATCH] patch to request new firmware for AR3011 Chip suraj 2010-05-05 12:33 ` [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip suraj 1 sibling, 1 reply; 36+ messages in thread From: suraj @ 2010-04-27 6:19 UTC (permalink / raw) To: linux-bluetooth, dwmw2 Cc: Luis.Rodriguez, Jothikumar.Mothilal, marcel, vkandukuri Signed-off-by: Vikram Kandukuri <vikram.kandukuri@atheros.com> Signed-off-by: Jothi Kumar <jkumar@atheros.com> --- WHENCE | 9 +++++++++ ath3k-2.fw | Bin 0 -> 246804 bytes 2 files changed, 9 insertions(+), 0 deletions(-) create mode 100644 ath3k-2.fw diff --git a/WHENCE b/WHENCE index 39ff025..ab7b390 100644 --- a/WHENCE +++ b/WHENCE @@ -1229,3 +1229,12 @@ Licence: Redistributable, provided by Realtek in their driver source download. -------------------------------------------------------------------------- + +Driver: DFU Driver for Atheros bluetooth chipset AR3011 + +File: ath3k-2.fw +Info: v2.0 + +Licence: Redistributable. See LICENCE.atheros_firmware for details + +-------------------------------------------------------------------------- diff --git a/ath3k-2.fw b/ath3k-2.fw new file mode 100644 index 0000000000000000000000000000000000000000..3fa8cff36301a7005936d54a407d6879923aa354 GIT binary patch literal 246804 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�a?<(Tf41!E2%9 zd?Y8_Tzwd7?k8zX*lE~;kiWHXr28kDCJNH0PB;{AC98+9dJ-@cvGD{J;NJrZ^RYrg z+)%f^;LuYr6+_bwV{xTf8b33^T48p*|1W~4luwO-NdvssSsH6n##DgKrEGBAdf3k) zZxC;SrU-LMxl<^=FBk^*RGUn~^q$ss)tm|e=|oj!Ol68!KwJ#w_y&pB$?bvhb2l*k zdqq8WU?za17&_;l>I>u9SmPgqKr|w(Iu5tH(ROe+jlT>n>r8hL5r+FMas6&#Km3=8 zAy(}!Dd*=58}Jm%*%I{mOonw`dWO0Ao#ws9NvVE>+~qPx)zRxRr&VMTx~mD+AN6;B zyz{^te86f-*uHd(vd27jR`1<VYD^Z@V8fZp{OqiK4~q->*Z{Pe@@Q%^8<X}Dd2yG% zVOPW37TE#oV8YEsc+ra&-wLIov#1?5oJhR?{o=K~&iwf!drrWFe8$Q3VjQQWvUHK{ z1%vGmFf_pOM5MN4TYbDaTjT%|!@6!+iJ3jPW<~KnPhx8RKHOsu8WU5o_z(G%8o2tE z8vX$X8~(mWn`(RBf2qag3;s)ELaOa|<kD$<(;lB%4s7Q}n2p}ET=4nFyLr|zzFf;G zmT7yD0cg;sVK(G5Yz{DpVJ_>s-Y?8Yp0$WgLw9L%a=IWH{Ly+V*saS<^+AFKFO~K4 zko`fD@;K;LPqYhp8VSWBk<<1E&i2CQj^=kyAB6)Dn+mrLkMF#Qd(Wo;kS+j1q1S4H z<I!h>%A*9J|GAkJLy#<;8ewK*r_=@2rEOm=v0exvUIA(6Mdu#=J9K9(jS?v*0=9@^ z1>_ugcK)Or)tp%Z0qz0L<>oxB)vVlW^?CC|F0sjNeE{xDSFXt+5wdB-w3a9Iyi1{m zsA`3vGI86tei_m~5A*;OwY|i6Kmm9V-{I)k4k3n8Z?oBDQ#ZeJu{mQ+40rlzyDz2o zMFvkD)^)J10gWg#v10}?US^rxLLnKBIVcBjHx9(Cma(I|Ftz5q5EswPnMc;OldMKV z&3THLlr~z%)*K!L*8}jaAK|zTcY0l>NS9BS7KeEQVw+Ou_&Nt4-}2%$dixp}QfCat z<ul*K64rF<{Z?Ra3_>=>{x)TQnF6^4KR!gMEuea$6d#D@Z$(2F>E#4*pEM_HQeG1A ziZBS|PV2hPNoLNb&dk94pi}u5(Ovi<82Rys=Dl_*wM7ULK_DxfR$ty*w9_E+Lm`Q4 z1d*R0u`BI_{K<bE09QHwdZa!5|H~<~r|l1I`Ha`PPL+?X1D*kZP|A4kfX9l49Rv38 zt^wob09QYbFDUrdrPP4flru=Smg`W_a9t5l3N^{E9ldzecqOGtoZsA(vPWdyFC>su z;$xr0LkCFN(xh|qc}KMrHILK>?9U()&Y(>>YQtkfKe}(EoM!94{!fWRew7&cI)yX~ z@(xzomdF(TywP61+2snPxPbTl66?9WLf8NSv_7)o;JxkB$DWwF%pGvBW`C!$`ASN< zTS%p#B9!U6pnqV(mA#@XWA$Y@0t*haLK0q}=9x}&4vn9<Y3h7Ixlj3`H{c-HU~i_3 zYA7lKb9KF+fOx@PA&=+Ac$qQCMvZ0F1iysWbG`gIBu;?731HXlq|M8tv|odQgZ#38 zmBMv}v~6Mv?-{>H!8C{JtxR&vB72XJ3h`R;^)F4>y`L5*Vm)UW&4~x562Y=n6VkWT z%ogl`zJl%<Kn{d))cAwXBBT|w;81~mN=k7;${+;6Nr*M4Q<(!w&iBia=u%4YlcnKm zr$FcuP;LQO1_b9u(}x;^`k(6TJ=&=}3P1M03YG-$le#qi&FSq6rb4X}1?=-W?Wq)$ z&r~Kr`B1Fk4K|V9b{@=aQLIW6Ksy3N>a<--4jxslDZ8o-c+(gFISu{9iVBZAX@nl2 z*u|%tm8VmiI}d(fwZ)}2YoAMI-?cVa*_aRO1syV0RXA?nyTDo(UUhx;)BV6+(+k#D zcUgbl<uy>By^EX=jRQ$x=Yd_EL15a^xjmt<E1xOsbI?@dTc^Ku2YniFcceSztP<Na z-Xq&=!2TJdPJ>4Kgny;ozE{R7dEYPOQ~fetZ@t!e<((t`F^KgSw(r89CSd6eybyXp zaACrQIOq$5*rd$+8=D^Pe5$6n$ReW6BI;n}_8B^hNKh2qeQsJTJGyH59)W*Q)HGU` zt+T!)t^mRwtUj_uVr5S!*gIHI1N6Z=qvvCHaJTZPzqHNou|auzY}EzYS;^ZdihI9& zZP5%tmEN?5ba+*%nWh`t+Zo)iUfU$}<R*h3$kagSsh>Fj%sFSE33=edngy1>``bHW zc_P_4O2qFKx=f`cla1W8%}*+G%%W~M1o&IwnWhA@=u?Ymq*3-o4tMK5wxM15ZtJ_G zi@3+SXZVqq=tr!;Sf4V4tQk_xLDNKQ*2J2OV{zC%KTLsWAOc6|Xf}D{fvny$HOjuz zPL8Ef)q~eKmNK)T@7+H&!Ma6ntx3T4ykiR(>tTX<+csFA4lhQ>Ov=Ivh&$fli!%nj z_`r1~F_w9A*qa{<S*baPt)9$g+6g+(zuxgnrS|<4bPQAIJDbmdNfSmP_O{s?-2Z{T zzca!8xd2Sf8=I6An`PZ$RW=i6ot+S)@fnk;4FZv=zVk+M*uh1h7QgtGMK+3h$F_S% zRIUIP!Yo`XC#F;ZE;y$ly#^jIGogHAZ?m^<zWwLv`=?ByyLC7jP=(i~90@6uW*t>~ zX_>=75uw8%Yh4C~D=X?8Oz8YdPh6r92r8?$x2?BVcutWRFmaA4Va^BoBM!n6vC?LN zU1?0#GAPdqs1hXIYJ*<8<Z<fRz!P@m5_W{C`$ThM3*EMvvG4R1UMsvL7E?>ufJ>$` zgvI;CXK@m3dztZ6;Tfx%GG&XuoiO4sn%N-3`{jkV<|Z-|HZmH~d)_a6e(_QBn`0ZB zV((m1_P7GOF+lw*XI`vSd9=CZ94Am-pD~#B`$J=SXsTEA{+01OrG$NSvS|ki)Uuru zS-?p+>llyTgMJm8B4j5>Q02F1NKn`|>NTZxLahq@W}$^_%Kl1vTLNW&+h_dJ=K<S~ zz|-QJ0jP=Rg0ISEbo=a}tYcYu)nzQ~JFk6|a?cw)3KZu2^cg&w4#8w&OE4wNYVB>~ z(KBe#i_NU8w6XWY%oxaBZLcu)myuQ5?-}jO02>*#6!Kj{-G~o33lEgAr(Cg1_XFb< zPT~6UM#ha;4NosI))5dC#yHAf<Ptc;mN>$m%AlQG*wf1F^ky??R*y7y2(<~xmM?wQ z-k)5Hp1$#fGz&udEa!)*FR|mmy5dCa5Gi#9c1gI92In}f)}BY9j7Pjr>ral;YRM|8 z#rEilX|&Upb>e7JZ(*nH4@?K^8J%2scZS^bPN;EovNLR12h-ehVxpjfZMFX7A(}Uw zqV|8RD7@R*(rIcA)vA&mVapC3kywv*c9YXD8-RrWaSq-(7wZ<InYS$CF=vKISoWnL zYemQ<Lmo2?fOODrSZ|uPgqR-_8utJ@OG*G-d$Qx4@0F`H4I!4rOD~C$>M4Nfv-YaF z$As)=&+9zM7Hxx$WdqSbmuY4Xy_0sjz?sxSyT{qCYFvinF2kp;q9v}Pr)--T>^Ruh zewX5<;dRK*2qK34$Go*apTaI?18Y&%QH8~9F#DjjGl9L;Ix)_FKH^UMHa3mMyd8Yq znqE<(^uZ=T4nc7+3=B`*>z#_<ieC8%Gqv?ceaUBc`E_-fDdCQnnqteEnC=dymaWjQ zDZvy=zaBzuw`EnNyMF_$Gqw2cV0yyfk9yO*5Gp(isf%){J6@#6TS$sTq(JN~tk3+R z5Jmf?VDmF?j1A=L0$EC?%ZOoPbed6(hI&D1SlGV>d}9har$u%^P++qULWrI40t0H~ zcXt)E9nh;G1SYF_HzG(vfLSs2{FIY|o`XF*{IU=sAkba~OI1M|P8oAk*xyXZXDak= zM%cLO=4+|!8uZZg9yW0BZM7v^==q&wn~1s@6wcq*nj{<pk@PMRNufXGMX@u;5c<$I zoOTe|CP0`b-7XLE$P(Dq05y1)N(fv4k`%p8#73cW*^O+V*ZKnq*fMx0<k>)P0XgWo ze%r|Z-NXNVo&Wn9|959+YeMH<ee+%pLDmAYtkyQ1#rMAvcf%0eE}#@y)FoRlAPfS~ z>r<ei`Oen=qFSk`Dy^EAda&@W){|uId|@Pu56y>>=PS~)e`GG_Gqyygjb?5MyldaY z-Lkd2@z)}6ccyMV6?C~A=nDaKWM@re$$`zY6AS3tt+@84Zo^*~IM|oS;9E<6xl;e+ zlpn$c&LNkVG3p*btwW&najyLnlzVdWRp(lQ9pTpQfKo9I&G0<V=SPe>s(Ot|jwbiy zlNn$rsl?xml8Wv&f|p}KcZyNmMKg6go@3qCp0Cl)fElR*&s7Q20?!3V$?4wtIGL-P za0sE-iP6@|0|I1YPJ6SG;7q(}yKl!i<y2?yzZ)`eq#^$_@kY*XnnEcFZqy=j!i#=$ z$%&Zgv-ynaj^N@YcVIV}7rZ2U^F5lI-Cb2mn%4NYB#b&VE}v~@>XzocA8zFa1l|AR zYl@@9BtP-+xyuIJG3z)z7o51i@S&II<A6#KR24!`9u|Q!3`rG;RK`JH<2Bnx95lI$ zK1=>_ttVdf9hTP)ITu?DP2G`ZdnR7Mnqk|*v^~$TDp1w^k}sAJGq!?C<ZCqe^WDNY zoTJ!SFo<J=YTV)Y0&d7$!Ke0r#k1cb)3Bv{rrOC8SJ+eTFML&fuBV^!%R10gh%rmn z1<oMa8MHL^fDW2H=!s3K+S|4joEM(76zqUXc8mcyO>5G|riZLw=&hXzSUFPmq&CAL zjQYWLMHbllmHwI=CxTY!ApRLbM1oyrnUwn4)=v(U($u4vsA~g<{WGwYYG^7$bTDxj zzc*?gJD7W)m-itjB{Bx#L4yH|P?~Js<%8&UAO|xBXS_Ol%UJ$QioIqj@}SvpbvzCX z<axOMRb*xmf@?dA@Z#W}{l2ep2T1B0GcinRw==eFOq+sntTE%KWsR9nqv!1|!@jl| zFs<<_rf<T>0&d}#^}dOUD^Fqyv!RGdO3Kts4b#wysT2JoK)R;2x}<`xPU2SqG~n<$ z4Llvbp;?usjUYEsmM#&~E^Kt+u47%6Z61RMwhE?B2&+-Ouw7(@Rtcxk;m30rZ5V9> z0D?RQT@K!ldlma{h;eN^&aiEjj4Fmra#<;tA;e_}bs553hH#f5!c~NyDwiSFWk_-v zM!O7SdiNlnOIXd-8fE>pZ@I_8nn}#%IBt=ml5&+VWiq66X}EHOf?2+unY)cqMdVq6 zQIIktX{Qn++6l8yxWjCg%Gqc@`>Hc^4#8|0gyHJ2QU`R7thDDP{|k`*_>1!DmHfNV zXN}~6Z%tUrBtJjgMnp}=jd06YWC^zWbWOe0T5m0=Ja`h7b+)|Dy*t_=3$bt;YTh!Z zAKU^S8(HaEA!nP`e852`_pCLJkLL`a(&GCOVWS#jz)o9z+J<ou;K)zr<mn!AsQ1_N zV95jpqu&OKG871$XdHLlevx=HXRI{i8WI#Ak7t49U;h45MY3YF4F;823Xrl-7Zp>K zlU|fFu_R|gNzSCQoQ$%Z$xvEc;F(1&!t@38@%VY(mywh4AE_p5a?*8WVLEd#jK73+ zT|;u1l#w%exE+jNfc9Ao9&AOixClNwD7;t|(OAaD;GQ8n-H<)8Bzr<h_N1chjG}D3 z<zObl2gAWXVPnk9WD7INGMr+vIXlA$zHHP7bks~b_$=Xfl%0+o@H&e3Iew4mOUo%Y z|GniRM!$oB4JjHFqcQEm!-CeG+SaEdTL;O`)jCep`g_qCNgu7z5qoS$a2b<PqB9gR z>B~%$mMxoP{`7}?t9bf$L7&hFwr%L)&*0MD3xqpXewJ0my0Q^IZ=y4Mf-`%PBRj*9 zJy{EM0%is>L?aJvBaQ4GzfBkQ&`3_Gz(`JnNm*`z)k%q3JF+KQvNJ5%lez2(T=pb3 z2He(QiL%|H567LFaAh~t&)`{RRRi+%Q+Qt+=qWa_Ou+U2>01VyMBD#32K3s{$bwuS z#qGvN$OT-d<mpXYz`&38Im%uFz@<U)yj(YLk<K`Of8-OoKg~H5Ia_DXKM|RCB{GM- zy*u(z-S4LBUY({8MRK1GDw-p7kpVibR5w}V`1IcVhrO&^>;C0Co=@-{d_{$?9Ncn7 z0G><YwM%>oaHj>>6SP-{Z(xGr?Lgh{=+ak%hF%3=ExBiDiv$LeGrolY{c8LEogV^B zk%)Pu3P+IDh0y<FNpEfcrKI)<3rafc-zDWx(gw)f;6MGPMg9uTY?M@qa=p8y12$%4 zKl0H20X$C>DyhGSXJ2Jpfyq5fND)0wbdeT81cn>d(>l_yqmj{jTR@EwJ9sNj@ku}b zu9&=I|HP-z$;{mV<VY>u_Z6$9WLZ3jgsj~7`7c@F&aX-YFNoN|lW_|6(!aCv9Puf> zk-Cu8PN^GNjgt<6w_^L?tze@6_j!YL^Op)+Bw8DY{83%du8$0WeIw-@9sqX#=iv!5 zlHwg-liuId_Puy$o5Of)d?G@%K2lxVNH)=^?tv{Ht3L%FCW!AQ&!2x?3(wm~!LPqq z2Q4GyH)iRk(x_1OG^UN}DK=2sBd*0ZH%mRdR`3t(Uy8B9aWPDFj9nhjuYFxh8ReGh z3Ja^5GL7F*F|C070Gk*qpMV8D*t^0!f!E7(E2_YfipTvIx42eR<APg3V7A<f*Bk+9 zPF53gl!#UU`Z`y<-Zey0aw`ZN7$SsYMJ2`q{8qx)3P5McBOW?yUQsbzKHE};lgHWL z6XRJIz8m0JhYi5L{_3&M<Y+WGal>gx?i*;q@}<m&2|{*qcM1*|Oc_m;<<e$auz_Z_ zM{3aCYPdG#G9A*k1>~bVUWf>)5Yh=`=9#T#ns$nXwkl?OW2jp!a?y-IEWWZwgacIG znC$z~+G^(PPUiA!%p8DOa$!+q9JwM0utl^GZY!|o6y=f<L$Pk12ds2R>pZn3k%+zc z6rj^Z&uoJd5nmCP(IOM^k1U%H?@4Mb(H`@A=-k&CAiNX*%G2|Klu4|Lt~sFI*sOq< zs6^zeTFF{j=1FA*2@IGgSH1|vsDBa0o``l?Lc4^t%K+Ue<1`2yLZQPzb3zekU@Sth z1s;MCLbE|+h!AGY)Ef+9)~tC<@+;YBHcpalHH=DSG$(V%3DMF$V{LL;m@3MgqaI0# zZ!NE7io)^fXZ{pXYhkj=f5PdI1bd45W7+WsyQ5GazZioul`u>ZL!02RGq31L;BAH8 z9NNu@+&PSk5xa7Pa5!-0h#ZX6kt61q01G3>`MS9tT&bU#Uu-(0k|icPGC8G&le1io zT$!`&_ON_6cNe1xrxsYo)xPno66~&1*_I-6#);1o-dtxf1*D22pSCa}OFlE)*0ks4 zgvK4J%^=9PQN~9T&dB4I!cn~i44M3950b+M{+WFSo+++9AW!VKFxgOl?LD7y?eM%K za^El9B^ggXGSZj-&2JG#o<KLFnw_DKAXGs4svtMw2zJ492Sj+kP$Troae!m#gPEAS zhhjq6Ou}(MW${Nyp5%uuz)?vMqK%=z$b$1b|09~pC4{Cp3z72lzu?wWH=fTd|I;s? zcqfJZ{~Y9*57<~&;bw<`Y7&m%7urghx|A~l_+B_T*oN5@dpk?URh_W^J->g^tz_1b z=o2|)FV8Q4+dps18OO3%9M#qrK<>rqsit5!rZ?B$cs}v$n@sN)z`|Mssk6Ho=d73A zvo@RYy<k(tAX!F%vF;@Z>#=O?VEMC1u-Q$X`uTO6!F>hRv<u_BvX?aiN;h6T?1Gsx zpqWt0EO-#&H;aa_vFWd=D>a@LzNq*F8^g_dS&ZmM4!(~z)Pa<URW7e*%6Id&Ty&C* z7||1MJg?|#U^)p`D40TB^eDf+?VH%~q|$7V+wgMf6STwdGH2L)OIEyes@6ciocN=l zXOd~6dYX3G7NB55DzzZd;|wo544co(iUApB9m{$`*7t;%2sJPn1n7(9Xdm!F>#3Sb zQuBr4+A++IdAf;H6<g$`@sqpWV%oJ>Ee>lr%+ClqA5<Prnam-R!jbIsQOCST-7-WY z1XmDfhh6i23$?Um-lG=L=IW~e@cAU*6j7|g!8RBA1h|ATP;!iwvxi=^%rXe5TjDIg zp~yEdUcZ5vGSYCNY-|=9ZoIL9ebJ&elZ3<lf`oYw;5fILrK?xlb~D!Z)CWNR-PMRv z;=5S6|FfV7SIB=9qViZdXq)*To^$xI?VseFffJQ^5Q7o*o+%_CBci|}XZ2u~urILz z8l(yA<M~|1rNg;x91T7f-mlFipiTngz=Lag%!QIBc^rIHo9l4Oq%cV9Q#AD0`-18) zZS9?OeqMY}W;MR)Xd&_&h|)ligBHXaX1T<VcaRcWI-)FT&1`kPCSPjYj?qWTg8{4o zCuYn7c5EAp#bPL<h_XF``vRgPN+SGoN$_)}&Ip_W$}6A-0tS0{4z=s9$0cwdx=Wuv zT9@kNC}=+FQ_a$`tgJf9xc!b_>$4L>ieKCR|Jwt+G$^onaF}1G`uPei8@79uI2j6J zG#E>F-y1p4BHe$iGh&i0sq_C@XUG%W*8^UgQ8V~NXwj1~{S!koo>Xd{j2<q_*?49) zeLfKNRoo9e3FctXsllE6^j^1Id}&-+in=5vu1FqFXtn!|6Oy;l2u26<dg7jcbf*#+ z=6P9WU&*s^MYH2i%F3QqXZ9Eh#qBb7Oy*H<L0psU>>ehw*_*Gfkg)}s72cVe0h!nO zT;_Gh<GPbF*VE$69!H_JNp?ArPZnJv1LGe_7jsMI9<9st1zdOkX7qWP+3L&eGtW*q zDLeZ%liA~fr+DF)O)|$*k7hQT^Z#BaYoRkiebZ??Mq`{|=X#s*Y`fDW%!GSjyuJ25 zkWB|+{zUJNv{eS-<_=|r<!H-aWF^DQU)r)396_V&Ks&667O`&t{3~JuhcGCBpWawM z9kzKgs{Y2YV&zd?afmK6P?s;yi=j&!B9-%Ct+cSQ%83N^@R|+oRA{iKG-%4L`$X1% z?&=m`Q785pmVx6OgsRL>odr3UCPF$exA?rtCR`9O@M@D-M12@f^idd69DlzPzKE+$ zA7+Sj2D;QNzzS86+cP6=2XJiuz9wT^!m5l<`p33?5kIyukmYP7t7CVU|C#BsGD9{7 zCie-vQ@@^KpdfJsj(5yX!>6V)NjNsWWe{3B{}$4lBY@*c;*m%1eTJnS1{*Lp?sCh- zFxcpxj@3O9@ZxBd?(OK($Ccfvv+R>ms{<uh<(<dXSce9ZZ(jci?yR%Z#6S3KZ!xdj zvi>>b(%ON*_Ghg<>Y9(Y9DY_8FsfvBdeQ7O_|R28yFXY~G&>#7>H_PZwb_VZ=3MiZ zBQT9^u%FcokJsgdyQxBqUjFDMU@Al{OnN-=%Rgc%nkBZ&1r@@S3T|e+Ruoq!OsVHq ztBu0AcA;|O5i_?cu8Gh(?Vrd4JTWgwUi2#QT<}2MSSSL888N0mW)O;%n(D2(1hEcE zVrXK?aDTI(YzY6C?;z#yJ9r!KVDt#*-lsKIdLtK+&T|0IsUSr5%83$i0O!mY%;G7g z$Xq4dRKkVeQ~KyrHfjT?=~$*L5&TqfSt|#XImFxfnIf?mdXmz~AtmBZMwQAX-$-i6 z5c>%&<R=x^s6nbWQeIGVDRdQz01_EE@vnlhqI0X{P^O}BH5zN|;4)3CMiWWK&T@)< z7)_xr!;zLyQIV-*Tzda9AR%|@<s8!<(4;&6P^p|btZNCde%1+T_f+V)=^#mjOFVe% zQP=xoR;s6&^SQ{~wX&ve{>j`9Sqra+U0DmOIBvt!5a$m7+M7eUivp|l4Q}OYEFpDs z@V<GDmE-{7SXpI}k%Pi5QBBnr^UCVYIB#wGUJiG?BU_B$K2WhLLMk`3f*A<qp6fdQ zP}^PxbTj837IXw2hROU;d$93agqmI`aM+@VOuuNC*7=~i0n#AIr4}3!ERj3JLLDQV zGFh;MnbY;P<Ff~fcjfF+l4&5B7mk%XI{Uu{-J_a1&$w5<zPWE;EUp1WR$mV#L<}qU zTwczcC--3R>IB6g&@?L>Ik5<;0CqO}$5*7z{iYfRnY`RekivxE!kw@Ue@`Nu1dq&G zxovY#T^cm;&#VrFzq9-OPr$W#mE5@XP-$=gl9tb5%quIgUf>#hGb@Rv+p)4D_Z=U* z<3uJc{=(Px4n$|7_XcuD&oS76+7l|Ib(2~vI;|BUyW1lWyToFP+AmwjD^666_UTyS z*a90m`j@>C9qq}&8i^2gMz;vWWTWq}#gI82IgBdR$f*wq4z9r_Ef=fOnl)7#B<xMX zI%B&5`D-?t%uY>*Q=lQ(6N$CPW@qeC<UmHoGRGKTpB6Dmlk;}*5j$;n={oH~;zC<9 zuM_5;#J>vXkg%gHKfTF2ulzilNq=lh&pWn#jMEn5IIS`U#7*EB07Mmc6i0;Y8v7gI z0f*w)w>@SC>|G2y-PoQ4TGaiF)^sbanhjhOKWifD=U^B@j_1ea)}<5%@bUug0vi24 zJYg9bvSi%HA$!^wa;N$<xnpZ$jBFe)|K;Oc2d|_I`&Xj*ryp=MV{8OSRtvpWu`1O< zu+)sb2F;^rxcBS!`8a8$by7G(uu;f@Z`{8}-^h@oHt@#J7Ulv85%G)enS<_mL=R2M zV;9{iKR^sF{_lXQuQDT`nKO_RBHGFP{*mpPaMi5D<RENrFEVY&6ttw|yp@3zkOyoO z{3|haC&)-I81$4YKFZ82Y7fY&=!83rV?`8DCK#B5M?<S;a>o`K?~2ayJ~phPB>T4* zX>hbz#2Z7#zI{6mzuja1e02i3&n$M0{ZqQJwRiVQ3mEXu>CIw6i5RZqS>2tAd!yie zT7HLKwvT8aaDW3f6#!_J<(|q1)obiK9ze&?@!+M}4mz%xS^r{<`bDcbQLt>Ex?f6s z8ZJN14Be-}SFvdWE446-%`2imtW-GQPpFJ`ow})*5nWmt#Y)Y;5nz`E7ju&cFCn@A zJwx{5lI-W(YeC}U$SzYH2zCVv6dj>u@B4-Tu?iMSo(shg<WeZGuo%tv{okuw_sY*Y z**G`~oyE933h+(Hhi{E>vFVG}xz0RL><LGm%WX`pmGLIMh3gvZ&8yzxf<%LAO3}<h zs93!mhbTw(h7L&)Q(Tn27;R$_Q<m&vG)!$XW2&gBA<lA~m_a2iR%(7RrOBn{Xw@!e z@fmqmUuW;BEeiHtUYOF&6mK+zrJGy5Qp!D((qtEZPmc}G8$;u;TVQLiJ-PgRxn0WG zaf*!}r3(nH*iezg#fg#aE4M8xMxp010OSYCyFvaTXY$^Xy5wTyuUPWKFZK4A@A~VF zcFsNIa|Bb8Z*yO{Nw`IvLBcHfs(j={G19NMx`Sxd>?<?qZQTgW7NISdzL-gGJBFB? zUIA$RsP0Y#S~cfW-8LT<r^iLze<tXG*TnwZe=mpM9>F?rg=7Ptr$G;i3CBnmHAFK? z+glsHhtJ~jJ9KgvSfV<Fdqj)p7U&O`*Kfcv!AXl(iBo#2(sn$A0NEFUE{wh~?!qIk zr=@O2{48Vs9Qr+#<?}m=%2+J;wT`X`H!X3|Vp!a$J{*wTfaTG`;ix@l-(@<Tde|jT z&2?HC`|pxkM2@Z~HVytzP@0%&IdCFG1>^J1W$n)=wMdRS*y-BmlMp4wL5JXjPCC@} z`2+qO=A?sLTaTxMm;}<_>CR)vab!bW^Y6v3&+jQZ#NN;E%pVip+WPfCZC$yGmekbz z-0Kqd_kZ1gv~yqs?z<kRn!3OmX{IHPxnhex$f*xDZH+MNCB_YE*ww5*9BQV!7gFcD z5MVM8>+MDOC>q!JS6g<rvn$>H2_Y;meLMs$XJ)!r4WHOdr6wS5y}2qgO?hd?O$$u_ z67-+^9chDs?H|+59syKZgSYgE7<Q+bstCP!etQRaMli={Ve`R@pQH7zd_;4n1!zgM zAzISkjHO8=i~5F!i}h#k(Y9Cyc3n$pvh_fS%PKJK+-0>cv$pI42eG0;G<1-0mL?6} zR(ng6&CCglI99SM!Wmw|y>q#<xl<SYgf4kGqAn=Dm+7LP(Iv0fO`FWcv7TJ$B4Jy9 z;^FKKP=2C6#Z_1=G0~jkZKrCxxBIBG?~#mqFQ{Fm$%wW=o?V8<?YHmb@-hX+6ZhOc zd@dK<X5?WxZmimm8>P|2LS2irt2yAyac#r(n!UM~edq||qiV8{8z-p`bj@!bu)WWC z4ytpleEwJpxo4Y5o`2_gab5vfV`1$I1`caOZA^J96UWO?Zp~I?Is&yO*G|o=_)84g zCFH`K+=;)f?P_etj@9Ivv(6!u&8s_&PsS_cQN~vUXNTwsiA?ziL@2ZU1so@}4#t^6 zRHUZr5Q4T|L#_w5I8t`ue=C?A0Hj?*t}Q8~e!WmxuGziOL}VgY%I+)o83gpEYX8cd z{WHnZ!6$X{nV1<XWdGg@fqOhz%6cn8YOaS60^;Z1@{E6=Xb8g1wkpO}&6F=7f|^&e z)h#2L;6MUtM15WGOI<JTB=?;%p-U#3(-Ufui9`BAi822*f&+k_69OWqArcQ7140<i z#6-7Rvdx(CK+iI9+n5O-GKyxR8@9oMs`20={b)!Qu*fBQam|%%wI@DkT!YKYhZvXU zDlU+{+Zxgj29%O_JlY414Y*ex%h=i(i{^V=<ExeuJ1)#6TRri3-nE?+(xn-~-}Jta zEb`_)Ts!gH@2f&-pA~e!&G#|xmtzM~F+)p9_hL~isqzLBIdt&q7I#VYWnOu0DIwZz zOUY|4X_{G@9m30g2X6r+wFUQCf=mDtMhO9AI7>*!xk@UvSr0IFaP3!b1Fe8%`XlxU zgqVT+0j2t7m<4G@gR?>aR1RzldtcB&TE+=El>Dv2T8xYSOIuvLgzV&{W#`J*u<;l( zAK+2f3Wi(Pr7aV&uP#5#yz)J^eRiCed2XzHaNMrM4m$TC%Jw)kq3a!TVVTvpzN%C4 zlIT$5lPv;!fi$t9kPWDv!(9FiGhK>gLw9ISF1!_N^^*$uoWZfK@oAksq2Az+@hwvi zz~`OV9DC2|t$e4Wi|1g-uWa513?W6SYxQwz=7x-}Nmd)>53uDapn4TYDc+K)Tfb)6 zxFP!T5@rZnF|*zqoVNaAr=mq-y*LQ54Kf>4wK#ml;zJ=mLDoewsMNrYgw5$wlf`9N z4CWCpD=VBUVt~#OJ~#u~S~OnZ<-7FI5bX|1tIjlwqYe(BR;-+4bzK0tz5lA(&A;zj zN!~A7S>Yh>v-0k*uvvAV_d(7EZ9kEp&=*8@%4LX;qqbhe>pgVl(Prkk#Dzm}ftg?O z!1x9O+G>h2ehgw(&zmAKiQ0~Lms92OOdMS60NH7xE2LAy6j9;ej1>3DFNAIrQ)MS| zDnf_inE?Tb7GXY_R2h0zaPtvbY@k}e-&$B`k4a(!(yGCcKgtuG1S22Ku7sp5=kjPv z9@7ncqU{J=CI00&9bsIgh7cUkJ3sV{NINPC9yU3dlU-jtaE$#U&M%lv&;C*I_ge~1 z7T{8hmFhIV)n(-A3<;%0A0e#lINMRCBD7Y++^VbD6=L;;zytPnojY&3Gw;!DSADGF z@=>Ne6mn5hRqq!e{mqDIp*~RM%$wxQo3ib)4;qDW?4xZ*K_!C&NW~`W-jLRc5G-$% zo4k2R?z~h0l3B(1P<HXOXB~NJXI?y(Q+C{{Q1&;|9`fczxbsG_x@~_W`6M-kva6@b zEqPLNUO@M0ABE;a57|Cqx();8N=;bLymH^Qq5}6^E2<pp1n^HQ|Cp)mV6gln>Qp<K zAC46!{N<i&<d2l@@n-xQS>VjtZxY!Qk0+5olb0W6rbg*Sj2e1e;-izXQqSL?i@EdD z)W|GDI+z&od|KqlL;sj4oXLv851gKD?ac{YoxD2Qw8e}ds;gzmvV4Qd^wX^9^cAfd z+Z*b5b8ji(BU+P#oqAUCj+|LyeLRC>`ZOMTN5&V%2(rR)EqS{mpfKOc!DB*0XfAD^ zFwVWNI8G1RA66p;|EMl`_#r9Ba;iiq$l7SF7zLP!1>-j@{U5FEv6w!st-`~xZjl(o zl*QM*r|u}m%e?UV{-YSgZtIz$E@sgvi;yu31%r1(2o{A$wgWh7J_-0JL6Dkr1iO<& zn6Z`|fhLl!daW|Bypu6$LQR?w_N3aU?vE#zuS~^`fM1B%<5RQrZ8BS(YsyiSiIyPg z^xuHt<z{_Yg&fK?qBN?wmBh{>V=HM}ALh%2(5gouhFOK4e~n?&&WCbB9M=TEQMLXh z;;>*wfVFADGYykk=rbZ?^#hWiU2rhJg+7Z@t9uZ^lZh7G891s#<aeeD08{cP&kLBa zv4f3(T8Rf@JFly+#;V5L3|F+1<|w|t?Gqo>9?QlPC3`IBgl$f=@lnU=zZ<n`4A8Xp zSl(<x`lYY{;2g9iR3QU$q6&d4e|$w!YrUCe8<JLq71434N=;u1pixmjW_2XUde2W# zEEaI%;_O$(fL`Pc7afP!{k8<<_tW0+((!&UnA;HNCsVjN4lW~cL!HdBPvHV~xr-tF zrh*-;|7b{NUsc})wodK}r#IKbZZ+5!!j6Yl;FTFTnXo%?l@>U7RE__KRc4t2US|HD z1F_sJ1-ijoQcfDeKVET8HW7Zx0DTfO_YPf{>(}qXeX3oQlyGDhCsVA^u)`kz`(hHB z!pyX$X^lTeYej2C)PjM)4%+EdLt`1_DW%#)M|L@7ztB!5H^J>%-X!-|*4o#4K6b(P zVI6dplTW{boax#{C54Y2*#$&Sd%}(6ac2R`Ib}ukN+%l*&)ps~p>I_*Dg9%y^@8>M zvrU=R&swjx4vb##TT&D7>5<&DOEEVv_Ko)Yiffbye|J!}!%WRJ#<E6)iUV~LhqXeN z<tU-Fd+)Q7#%uPy_uJ{l=Cj9`)-#d4{}Xb-P<=`AZGiF1`(*|~OYP85XDgVwI6}^6 z+@h?md5jx}i?^c&!VbMgi<eN?l##JH!Nw*Jf`f>B#hYNah?2dCKD0WV|H%#dLT{60 z!kw_sjp#$GBlzpRph0yT$*N>d*l(UENA^=zlVp~JcV-L@g7s)J|E$S8FMxdX@vx!e zOzXqyL$A}h$rLN`ym`M%#<V@4tJl<i%FGzty+vy4W^m@5BF{XrLh4e=T%a;qER#Gh zbt_S9fqOAC>UoJ*DTJr&Vxfgy+*#jhmI=%!Zz#@(acmiP`i5e2%%~MImokkVrR;Mp zPP_Shw40WCm1C9Hy?+yU7mo!#-))e7I1cb6!KIsQVB2w~iBtjd8&{fD7v|Ma<tG@N zfK7i6*&aM^1B47r(ff)Rk*69hjSd+)$qd4<Uqa+?M`;%R7OiaNB#VoliCN#=%E=k~ zKYVSc7*0d-wEz6TSb_Jd^Se$nC#oc8KVuH<6c86=bWS_X^B2oO^irVPG;<&<7i<fF zo8mX9+HcRl{+(B@Qc1VJPItHZTK9+)7rxyvkC{-x*dHU3a9JtA#CbU1Njm+bki+y8 zSWx!)!~?#8u-5zF^ts%>Q#Ajj%e`-f)RP)IOS1;T@X2zr`ExSnfWRfnvKFgL#|HV? zByg=;&FU&;fXOiSzlzZ_m!YXZsH`uEk%<T`E_njz2z&di{e4zftdM=}O5hk>%=F7W zO#c@lS7N0@XPChsJLkWD#jFg)Y+W2Ge%}|+F#o_e=?8_1{BK;ii(U!cb^vAI*yni; z@)IjqH|}3|<NjXUANrin<GHWQ$}o~gnD}jCW5f1{N?77wWg}}$Ki7Cgl1)jt!NqU0 zej>WLnKVe(5ba#C{mN#die$%Lp2N6TY{#W*MY&?BRP|6<%S%`za#^Z90bKV^%*rMu zCSFq}rru~{BI!?1?NUG6y!>nURE_(~8kZ<>)65!m&WDy0i4Vn`Ou&S8nH?<)FZRG` z@6vrPWz^=LrZwnD)jty-8N)S4j3jApB}rV(?ac?dl%$J2O>(h^w_z;*kGhPiDff)p zlt>C3$i-LUSn)Rz>N7te@XBVQnsQs|p*f6~hm*Khm}|C(6j1@HOh?Nb&X$F)=JW36 zR&O(zj&o^7^^@u!%aa(rhAGm%oeNsVcHJ-aY}(=cY*JQpNS_D}wNWEgy@{$4@taij zt50jb5KY<iWK9yPdR#R%acbO2-T$tvtL2TRhKoJODi^_XNS!sL&ciAFb>=IK!ZmH? z6N=ru%xL=?qy2Qu<srI`$o=jWJGwt3{aX8gu8?7KHm{~&<I!IC%a?j;1D10QgWa_p zdwzXrh4iIF66Gtta8^x)dB=-0N3r775vvnc=d6CRBu`e7Cojp1EXh-p<RzEnrPRGJ z2N2O0O7bR`<ULfebq>^XTTAk0l;k}!^$i3&o%RM+Mk1dS595ucz{Imk<8flQg)nSP z3>{0rbGu`ralb%YG{y!=UY^yMLXvs9{K**OtTB9g0Gf=Wcy4Uu2~j#Sv_nqRw+#^p zDr|`f1a)e0VD%S-0)V{G!kiImOAyHfa?WLNCuo+3l?mxH;yug^@JX3vROJv{@=Ej+ z9N)4Mtpj0KLluwDzObQwIU7?k1lrO$oe8J9-ff2|n^I=Ia+hkhL8D{_XJm8wfy;9~ znB-U<+8qo5x|i{^jPYs(tHTESh01fk7h?~*8VSS<wsXH+aiT@=`@pgsn*WS@xxg!= zynnl+_(87-%1vL;dMk49RpI=Fk3rVrPWcq4r#WDS|2EhO6-QwRAwug5)GeY*SHIzo z3xVi@+MW+I$HZRi!@Mny{XS5)I<|C?!5pZ#3}Jy6z5*)DvlZZ;90(Fiund_4NB!#2 zDE`ro>tcr5alLLF_x@dS;QcLa2@>lz1r1svbkk<(qLWLLqu~*ZK4}kax`v2z^N$Qc z4_s1^y6w1+vYln@Z;!bZxvdL}Nz+h!sPVF|D~~xp<Ya}H^O$puUjY^JINN;#zB&%u zEq3Rv(2C_%U8_~A*%G}J4mGzYnDsL4ZaL;O-+wgeKe{Ezn1Woz2Yqe7WtuLZ3;_N8 zfq=m`BL>_5;&__YMaS!sLrSM5l|P_eQDpO3I11+=jBazZ0?n~VfjXg2$bg6>8Afto z-r94nVGK&{2*|4bw*Nh~!zuuem+>8mxC+BC$QVDSyMiiB32g~#A9BV?Le8qSM;?f` zA|JFin(BPujy`jsc+eYBy2_}_RO=dIEy(rS{^&^EtT49YTy!9NYkRb`CCEN{jCdo- z(=&C<K&UH7Y_a~(+uXT78P7bbG2-W-st>r<;vG#pt%HL-oz3q<EtiGkgv0i8K^Hq) zyG<@G#doRdwI_$j*lzTX?PyY!XyhM7+Q#Vf&y$Z*N*@hYsSZt-o8v-f4C*T4byL-6 zAD|7Lu}Zc8aX*$Q@l_m%l%&3YlHlF>zCFREk3L<RGI504h%VifXe$OfPAFM38m(je zQ8EzPnA!D!cBoQYg20XaRH`<Vd^gWmq7NZuz3J=<xi#d~hYmfUug-OkQL#IT+^|%o z-7H7C89v>3x^#@n94M(T|Bi(Gw#3Ac8pNOmEEzMCz&{k^3BYCd1b+C~zX>_eHV_J( z#~JylvxgdFcq}8|O!2jVoX<6yz8`eE_JYk5Jf@<&?rfj@WScXeYO>YT=`FJb+-%y~ zu6N7^o&Vo3l1r0<2D%XK4rlz?g73z4gFU26HT?6^)I!;y=}Dpun=NPxwKJp)A-?t@ zv>Vj_XgD+k-*Sf<q!`Z_D~>|8skZ&By;q9zh{<3*dlxm54p#hyR7z@onsx<dVTe}u zwFKdW>9kVzKS;XoJfL*ZYTdLT=wp>2rUM!CVmZcYpWG44veVJ7OIQ@`zT7n?fK_26 zFAZo~-Vwyf1zLv>ZS?H}N-<jK2og69T|?*=(U~9|YCNCZXFod8ptZdJuYu)eAw4j9 zSY|&D^I>{L>#e9cAom4JSQHf9qX$CgUq9@O3qRO{rA3vhiyo^>9;cf&RW~bBw`hiL zbrh?F+;MWKN#5zlpYSk;t#^S`tLnbxqq=YU;86^Gnd;&Gj$L4lQ7sn#=u@plh!jfo z@^GwE604_8!vK7iW9W)#5w<!)4hz;3U#RuGFVuR_k`T5GAq-0+WE`_u@j?MuuI=BB zo?~vUi&AP#8o}!Dl2r(HFSM)z(2xNKhClToZcG-Uy@k)0-&dOFI9pe<O)66o;mCkD z61PrpNb|4tJY;7NJe?rdm&12w+T2;o5o&G-#p*#A_&n7q)lL_QE~hH%iuhh~MmFdT zxy^%dQq??n3cxt<7kNqIKRc|iw62QkKM-2I&5yVt=Dai(r>nLvm<cZC{PHG&H<q>@ zXq_2zA*$0SNcV+gWeHpV0vnimR!m7CIen4yVa&zSs?yc+B8k8&VTK)u?VaCUQ+oWT zx8cKFp5W#<r5+?7@nG@FBilBB|NbwJC1DDP98rtMN#@nVw@1!p<rLx?@dq+Qw^s=b zT_K3(k|ZK0WsQc8zct>ZF=yyQQhb`fsc(%*4!!l<ZGw)1xRcHY>H$2eDO7)#b^`G5 zwSTX&Ua}^|vcV=z=X{OOB4^A!{DHonKhx(E=j7c0cH|2x1ge)!)vW_jPC>|RX_E*e z2v+T@Q9MfOcC0rim6(H<-u1(&T6mBo_HA|#`xN^y8)1Rdc+ialZ28|AQzdL#>{Y9j zs{NALy-N%S8&YW%e0#Kyj4>D>$4@I`VY0;Sx*|uI_*NOU1uVsC&|0+qWDa9*mo-$} zmU_cPjw12Rqa+HT(?Ge4V4%(@V%ftM^5V22b`75cz^v^>)G@@SS065l9X$IFAPYNT zbIB+Yv43!rq!~p*ypJLbonxjMZjC8F&)f{3xIH{wCD%^&mnWl$nR0lngQNn^Ed8Co za76-V5&cn2THyW}V-G&Xj^lG;f8*sd!Z(3SJj@toM)U8WPW*HJ2kMz>AMei>EB`~~ zw8oZ}(WNf2O+y@IX{D?(x-z+PTIH<DMU|_~t0ig<u@coz8>Ov}`zX_LT3~JXpr$r% zUv#vgOP#)`1}qg3l<_Lia{|M0S{D<#;Eiv=37~NbbV?EQMu{JORDJ;h8Pzzo_ArPr zqpJ_w&cVdpb&+Y8pHPx>a@+qfhl$`s=8|ykCFXyZqjHnRMynIdDOxU!Qa1cj1obrX z{V)N90enAIql-~Yv3%E!df}%oMxxAtlIz14x`<~C>WcC3{{*H*Po`i8p|=L~17~>x zqTsdnyb|5AvWk<Fv!Atc3!7<PS#AIH9{UNP7+uTBVXF2HT?PzCNhrW}tg<bT0c13; zk6B+DG9cTqf+@bvJ=3+}Nv2S4re&;rep6^gMyXU>3y6cPKBM-ZpyY#FT?9zm`zy%W z&{P65_<Ekdz5E^x@)j4b+XtU~UCH?}Byg>)-4nQnZVJP-sX1h4Ah+@jXnbkJV6JWm zxTbhyRGE#ZPZ;I7;!_1HcbHx@BkB6}Z<MWk?Ipw5;IWbyKMD2>0QfRiG7Nl4-etUU ze}9HxtlU$3Zv+67cm%%*`UUryvw@7d?du75NINt!j+L($H=j(wH9`QWdqQD0b*|il z_Gl;fprq;qm><V`KwadcjaP_8jZGe40C6e4-afA8O?7LxP?07F3B33HPoQGFJ<e3G z&P^cQfE^32DZyTzyxYsG-@Pkeb3MMPNq+WUOmXu7`GOx{)5-&Ee*H<PyZm4E{EK|G z>j{G7#djTaM4b$ndz7lU9_MyEYi$;+2RV4Nnmvj?+{zsm*``7{c+eqZ*jNUogdqy) zU@(xs!a5SR;To^AuMlndO7e^2Z3LE;Fw>M-O}XUUjWKUPBn4;%O9m?>1c-&>mUu!R zPg44llw)LUWX}=I%sN@#YHd+A4m?2jAumg^fxYVh{c)AVaq5;LRia||PnRo;L6}(c zg>XJV-r*t$x#uUg2Xtu|KPF@UyAxQp^<w9RY-hI2^O?_WTe$x<Iu}JI?oX;lEhY4v zx_={c1Dj8Nf2Z|fon~WlA*QC-dl`<2(0*~#ZYQvo4fk_HDPw;^<3*IEPEt-YYwFEO zZx_vqFqwY#3n8$6n-^gSYtJx70zF|<Z%N!t#2TObH>&dzcKVt==WMaocH2Ng+KkNq zd}FtaO*Y$Zn~NF9|2-dIZWRkP`EsKRRo7U3rQrg08gAv@AvUh%Xecov3HcV(Dd1qW z@c!@R3u0IppSo@$tYC}8Q+l85*6`0-hk=6luu!Q0q1|lXtm~kaHg>valP(VQs<yU% z#;(7?rWkUi(-dX7)JfVapv(%<E4SG|SFWu7p~_ruZUo^+D-7z#UXj)zQGI7`gb>5) z_t9Y*hV*0}0s;9ySb>4`Z@)n{#S9?fJ4|T`{|cJY`0-6ZY@WK0>XWw74*H0Uh4Dc0 zdSV!lgM~p8#$PYT+Ni+-=?~ZO9}&7|2r>i|8YVG3Yc4`##Sd19NI)Snj7q1DZDF(% zZXrPTjye%5O#zry!p#!17qyKHiOkDrkKOSD{}J~OHSu3}@X-zZ>u~@|kjA2*8+GNu ze$WtQ28|MyYf{!~cLxm;RO?=|Cq!!{aKjN<L{AZIh7biK&F@cRzn#FR@xQzLG8Qtr z?xBUb4HcCS8E68KE6$mfYoLJ^9fp=qoEmSSh3V-+MYiN*Y5SY`J*P@JT2$A4r1`*` z@N-#s1MhZc&=RjMta#$I8&EsCbdh(iyGz5Iw5)w2oHI%zIa*$~4=-#q(*X<rffx2= z(5zP%f*1bbrUPBNaPmTk6UYpmg1iyV(ZNTWxl)9;J3s&Aw*0;e^Fhhf^x^5Fhqqa1 zam6<X*7#u&Ey>U!V(Fr9ytLG<i^%kOOG90>3_$wQh>`Yz`F1P@p8A)Fs;>N*uKZcf z`~qkG<Ieoq&iq1W{!8p)M}B^r8x75!Ug(*l!`m3!wy$7+b6R_iOLZjLUfctko$f;H zX)q$`1?I3MOIV^sPn-3O=SnaTyG4oaq9p%!;R6zcC50ssMF9($7211duw6n#v7?9i zAd@M}c!s8BCDc>_TtqaCc&D((yo{Hd??X^BtO(TzLJZk2os?jgA~U9^A`Kq3pr9QP znb`u#fTS^>!~bi++_6dQJyv-bzC89Pb!@aD`|*>K;+}yt7h8a3S20dhb6-6W>Mbnj zdECs*y0LI6z-M(5Xl#q?E%h?LlY32FoL|)5pBw;AN80#1szWY@Oo(7ao=v#M3{e^X zKs9bQWan=KLJOSS!(3y91%~XIb^-P8!jlZ_8}Sl3mtc&hXT8uuV`}^x1Yh*&Ozjdy z`Pa;jtq^=%B5M1QU|oz{mzhvnEM#-YdY6zF8}53HHav!JbqJjzfqe>(MJ2<VokUJT z{P?f^&pB?9&E(*I8?DkMr|PCnFP#;SgUfzpY+CW5BT*>w!`hpe3=Sc+Un#rsuo!nz zwZyXP5DHYP{mP#YO;?dro}c)8v}(zI<>f;&6r{5DA8~K`xI-WJ;+N9y#jnCo4t?Sz zdGOvuz&}kpJA~4xUzlDn9T8F|Il3mnu?2pGSPXo(1;htv>@OjHfVvg<M_aPaKj{l> zunec|6dI@M3ToL3wu+sdmgctl+<KvNE^|`g(u-Qyjql35@`cJ~?<}FRzJ*1?og0Cd zmoEd$&n(t|%ge6v>Z9E3YPUXmV~flzU!!b>1xZ<7&_=8UdQDo2B`wyXkGFq>)67FQ z5WC48bbubZ6<RtKtdP75&%|k=RT<L+_M<*dFXd>kD+a@LlGDpLIuQ6NExCpe11wdj z@i49tt2WF*N7~zQ&2n_OgH|A*7N-y7=m-ZrN=vRmZ%CeZg^+1=+>@-EfGKs&Vm)q| zE93Mbj_@QdJP~fBu>6+L(uKlhr6ly&nt96sb{35;(uXda*IJ~FELuJed!Bc^)w_JW zo0hrt5inJ`^pQ?Fz^PX_P*(jY3ms_DN15p$vp(7!o>WE$m+525!V^mnlu55F37=R* zhvGz96rSOw!@T-fZ+N<!4tMKEyTix3XhZ-W;|d?=q$8brwKIGSD&x?{Il@O<=usBE z#ynS9HaEsSH_|mXqGWEgcdmld#}~~-EQkrtx#3tbG;>Ee=7!Dvis8Ec$eo_oAtHVz zI`!bBn>ylWVwdY$Kvai>_Gq1&pY&YUOJ``uS%KrTm1(S9s^hw*rm_6*1dl{V*yLUS z-9=I;GIZ0iV3h+a9CtdO(875{I?j5$+`~A-<D7F#9AQb0xhpKR)}lX&W-=#Pz))t| zG0(g_-MA+htEa9q^<ZU_06u7SLhBJ8M_ccb6^!~mqCHu%R>~@?y6l8ej9hq5VW5*a zT>CZC081amDs<6-95kV8q@|05>_ed4sr`m&c!NBO9DXF1kVlirqbtLYg#4oiie^zM zs5%qy2zo0H6otk-+3)2aZHBu}>!+c;e}`%;;4603;0+S>rvKYrKt!othoKbEAh&fH zkVgc7l@GTNV>=KP+F1}uSY7u&eXJHRFgBw5DsMC4mzv?2pBc<7C)R}c(YlFBU3QfD z*j>ed$Z`xALlJlEwju@nz{<s-vl9wjLi$95nEWOSkn%(<)7+vw>Jl<1_Jm^1`;R-~ z;b%P$W4&ROH@3*T27Ylj197QP=n{%fH1xnbBLIsaRxrjXq_B5*BxqRUn+fDMKqD=L zxXUiaJ?)?Pd!pfcMiK6LcJcRw!}qj8+_PKwd(7}Xp1H_a%ip6%@*}jp+8X|zU?e{t z6or07Tl{J}bex`9a(dy3H$rBMPW*|Nj0!(m@DZ(x|B;Oxx<_BW$~YTjF8Sk~C*%D3 zh;tvv*+2s}&H|xpvGD9=#^zyeWKS~(O8r9Ti6#P`XJzKV085Ic=cS_A0W}zWv*m+} zLd=20XJA<cPEH{RpH#HQ0{t8AAfY)>j*G$ikm08U`etEcKu#rcD}}BAfvZ65701B# zn)fJ(>r}!0&1&#HArkAx_rpL_Wi|O}sz5fjgSbR+0rZ;Z!9P{^u(H1X;TzBAxg}u> z9?_r6OsIJH*2e9R#&Q87i#QaaC-*0Zx&wqRG2n89p|JnE&MPgje_#)kQ(g&nv73J( zyB})|q6o9fjd*(7UwLnCEkmt&-Yk)J$n&N~StMay*O&#<f=?56p3|SffA<8C{O_Xg z5m#Hzze?uOvp}}1-qJ1$SG_x%FcK{dUxI(e?;=0cCAEFSRDBkp!47f+1RB(7%l1sb z@1V5|D@_*k950>5REw&gEDiggv5*j+rMHdnp!oCj6c6WVEavmnUWvT1QOD=0{8gU8 ze4ZgB&v#0v33}$0mi7HRN1|gW?hs(RF2T@VmA?LkG9u#;!wh_Bmc6iS8^1lwGmB;! z^g?XpME+S2j18KVzt~7Cz804~CU8DR*M7?!nB1_Nll{>mGo6v_Kb${386$5|oGL&C zi$jPi(A5>95|?y^oC)j*G+})lXX15WrV<J~WN1HVL5ETPmi$R3ewADpS0bkHsTQ$A zMA<)tfBApPyXBboBR*eA8FYdEJ4UiXrlDu$L-~d|p`mB$zh}Dd^1h4G@3xd3zppqa zvmC#tm?r0r-%u1w9mjuG+?6<wdli?&uH!?B--|27m5{Y>{N={w>rDUG)`1{v5ao4F zE^|J#Npu_CV0J!gaT@AHmAK}d)4+dySabl_PUj;or~ZQI6t3OQsb1&9w?$WRUFMu& zc4k$IHsRXhoWeOX4~Xh<?Ql+WI;Wo!9mciGIo0ia_^Rjvu7CZlDHdm@|661%O}I;E zFj+lcE&qX8sAGRZ=#0Ebq3%`a=Vm@sKFDNcPrC6JnQ1H0=0!l8#|jmb9wdbwD*30n zFhZ9=mqvWaMxdX~V$ni4tNXk4N{Phqm^yY4%4Jr#4RBM)!5e-csG0Ia=%Xs4N+by7 zB{FRn*7!H)wexQUbmcQ?!7h-yB=6oycWWs(%@$s}l37wgn@p4DH;HcUTu?XVd>DaE z_x@~Z5%@(-fPr?$JTm8Jo)r10<1JZh`*0)|?`PlLOc9wsi`d^Z`t-+^fbL=0^Z@ND z=x*D{aH^*q|KlSH{HL1I&JHm;c>>ld>sOsX0%C=@(h&&`Hrrjy*~`-E9iDHbwO}p0 zRhe9u2!W%fX3~wrtj)_<4_KQJ9?vSr&}g5+Hnx5+>4Z|;Gl?hkZPKuT*3<JE3$53* z?$7V`@7Z#5v&<VF$I5`hcp^@5;9iTkPf&2V?GEjffCqk9VSlte_2d+yPB}ISbT4(G z)mL3I3buq<lv6wjW{liP17UT7{&$6ku7_5y_Trh(JBu=ht6?j9LVa>d?N^A=__Ked zefXo!k3ML147Ul~IS}oxr@@8L8-1=tN!<d<9X*o-<d|s|Px4w<Y=4v%pYfsMoJhrb z&Pp}ilV->p5d@UIhYqvkOW$hNZqc?do-aYAg-!0Br&fzG8$Jf@_{12D!0^$qfjlwV zzu#BSBVxBlMqw3rCSsf}T%_AVm+sOw(J-ySA99DXMR@i{rakOrU~B*HT3dgHao}3d zsX%ah+mGFv`vX|6IFZmH5|uV+Ar-e){hMGTq5M8Q=(C!9R__=gTe1Chkexsvct)wx z{eRTGd0dlM+CQA!_nrL!2}?i-ngkMd%VMii?XVRUmjWs(9cKUyRl2F|jJ0E*LYxQD z>J;L11eZ>TrXnp*A)P8h8AGN6Na>7Zo?ol2bd*l&($-?!a3jC(xr3dV=kxi!@B4Y* zzurFx_j2yDpX*%bTE17L$hZ@}mLZUvv@xBE5e^P5a5#o?dDxf!B$x9fkW89yuo`0j zCF)Ms0A^)24&8jr^!Z#}#J;@Em%<xk;cYFs98{S}P~Ur2>x|=(oN*@>#xNpWh_2ma zd*i7Hr)C3<nEAEY9KUTKxw<;<%Frc^i^v-TTc%<N#$ek#kXBEzyaz~oV>aSUj14I( zTfz@4bcE_%p)r7R9da#;aV*nA?kyHWt@~t&RMa>fyV%}+lF`^1A1yiZb~@4)FMe`P z*KuqIf9J;s(GESwr77|Qa>vJcWKnv7^xanbdIMVdk+gWNDtb)4|Gv8F{*jOVH2Ces zu0J9JUb2pIP{IRI_1$gWwKL!bPus7}4wY2MjG+<6*Z|^<9y5jp8)GG86m-#nKO6pX z@R#mCmu3r$=^A5Bw9zt^d@wUOn*0NijxnIY{LY{3M#>8RR~j=^EcjA`XM8{3`qzHO z?zAtjq*2^}ljM~8y<Ev<QA;caO>(8gp~yRyRH)BGyqPJ$o$SHx=K0GlY;ufY^6t}H zbLDL!9K}-TyIa1vE*+YfeD3&1DqCob%evOtYBu~3V{L!ARv>V2uO#n24*5wi>c#l$ z;;^o@1&h<}GC4S}uvxzbfPvW&EP^$_&J6pvG0D5EICbZ~^7%&*4mNU>opS9Sb9^cR zROGQo#BH4Y`Yj#@s#&1vBD=_Fy46>M=_+fAZ<>`pU!<SU=|<V~`Jp|GL8H|ScHCyk zE>o0N^F_xv@YOt#TFsS?I}~XRS9d$^;;ns(Hd)?rkG_r2CP#MMXUQTHt`a*Q(6`~* z<jjr<Tp4gx(BVTmlUkcx+9ANi_k?Mat2%@n-s0-j4&b$E?4jD^ogIjUy~q=yP2ShR z;h70ntsNrzHdxyRXyfE$&vHXLHN1c};7#&Iax(P$*+N&55NE_uxge@_ls_Zl^{|+O zrC-3a?trRazK`>ul%5B1SUf0nWrw-5L&?rxw#r5o!BO3#^q82O*n5HP5qjYS1leEm z6PE00=IlxPMi;r|%AV@Y)_Tqa@^ZI-+jDam@qw3%nT3@hG7^*es5U)kA=Ee42dR%L z^Y7yzf%)3T=)Pug@Bw?3X~2J1u3*<PN{>AA>59ggz8q<Q`ql$*DNj>ujwt9gxga!4 zaHx5)!qXT#SCJyOiIj$>*yr&-cD2IP7&|QCdZnC)qIirbdPHqv`}LS%8FZZXIsQjf zUTTVs7xM2ZxX<IUixrrJ3^?q`XRKjO?64f)m}bY?cg6mQp|>#(m@DRRBVl;(>8N!Z zEbSIqp?^bKaa6{$R&l?(ltQfz(LNkiS2KFGk!RypIE1U2GDEwbDTF>18d933Sy=uD zRtMnVdrS`Cae~u>11_!$1~sx7YshTtp+DQ^<yz)p=~*N>MG8)RG+%K-{i`QkrzT2B zf-N}Ev?F#HTP^WSk43;#Te%8{a6=ugA#e+_A(Uz)1=VeB_SEuibLbEwEX0sJd835= zg((TCLv7`d%PH-@#x)H}XJcYL+a|lxkT98Ajc{L{ul$|qiHjW$pdA!9`I3)k`BbcZ zODtw1A<?#SKtU7x&v8&f04;ZLpshTDKKPlS_^<%*)t-(RM{wY4@+`a4dc^E$kFgDE zO<M&>LZUw9klyf9!BOG!@*c6Hd|E1>2<c7#MbkkB<xj^Voqd(SR<1j$vVP`tLFh32 z;CuOKx!&=~>yA%e`#hPceHuvE&$KQW5xUFck%Nl~u^kpTGn}cdv|!UwKEs#)B}3CP zfm;_JwftzxLIg{We<pF@-!pChvo*;xS|008HoKB*%0`^dJ|_wiuLrb9Nc&am==9f7 zfiE)p)(SAHX$mg;4{Wh6(x!_gN3Bgxn=YzwmD$>4wprwbtH#PsIM1(F6@s^IHJ?7A z0vqBepJK4aK$6n_p?S5u>m7$~isAP$4S}5dec?3^&>mgPR7{ZS_k`e?T#vks>#o-G zT~v$Xw%+$k@L)qs)f_LrUD&=1@6C_!_@O!tlv52soO|Sc{r9SeCiC=_D_C(s$-&<Z zhiZJ->tnp{J4cgi@CsZMB<uiWqRC=wY&i?uISZYC`m<|ZuH7PPVKJiir2@|?0cL1a zlX#i0><hj-Vgai$s?&MPlb)HL_k7T9p}9N;osS_H%<~Vry@B?<VfDWZx_yoys&Z5p z<JRepPG5L+v^?fW+o_83qz7_u`82Z*$mmI~;Rmjv*Ih%eIR~wlWOGuhtvnXvP~GHo z);agKOv4~}haY(V`ugb%r}wX~ja~5%$TYD~03CgKtoe{Lb8Y(Jtf>65{12SZsIszk zIpvzNva+lUC!A)@<<aTo+KTe2M4UR)ob$1>jYX9``}z4<{4Z5CqziIBR$6q?W}UXG zs-heNJk>lf)|z21*Y0f`aOh&qI=v;BROqJF*O%?AeCG`&AJE7FWLNrO!Kg0Qrqk?S z(z>d?%jqajEX;tXz0*~0K$-2wF$<iwFE~eeTzh+&quglSY<8C?@D(xb;pgju$%=@u z089yjs;XOmh;Eahq9(piJ@fqUUAjb?APxzV@{0egb?FRdJklk2b#bFQ<K4|yGLlBj z1Lw`<iTx@2YnS>QOklH$Za-2%{u@4fGChyOzP957)?(a=>-HudI;(PLvfQ36@T3U% z)YpQ!nh>rdlp7A?;1`T^qs9jO*2Cqjl3rMh_4qyA#D{yXq}QWNpxa2DbE=^$^*ilA zS6cZdDqsi@92NCFOZ7>gay_8d_TPg!m~+>ZDKLcz=E{lOGh1K}6~tvqq0O4j`vc5O zmm<L|!5nY&m^Ihg!&z-kYpcp$9b<nic34db)t(s7BeAc^4UY*tN|^Z&F$syw6mGq) zJ!C%pf%(;?N4fzVoHdZWSdt$k(6%{K^Uc=gvVwWqPn;>7v$yQXd$QD6Cm5G>oDks4 zC^Dr9Ot7QX4XC0j3vx@Bjti|{n@8lg|JZv<h410xs6z(*UH@Hsm7X#EZSh!uyPYgI zBnj+NhyfPK?TG@2fwZMg?IEW<89Nd9Akid&?g$6ay!~#><gNsd{Fvbgw8dT<V~B}e zEKiZ!jRFXQR?DMahH}jyFtGx~T1XPa27|!P2vUFTO#KjU;APb{Wyf2%6iF?f(Fn3- z$KMmCK;rGuqb;bL?iy26NXHGNim1z{aOtshYzmiZi`TwcmZ8+fXmOjq_RTc7*#c9- z?^YH27Lgb3$u8W+%`{!DiR4yM`(u6Vo2hyw-Mle=2Sq)cI!Chc1D0kiCZgsjZr>aA zpEyqmcOXPOaD;`{A$Srgcch)t9axIbZLxHkH6j!>Khf6i#IkJ>dLjk(n?7r$lTYK~ z({!e7;As^=9B3L9EP74XeXW&I%j4eqfQszIZDQ~hWifR3MjO*2xteIMLBr|(K-*BP zx8fI|ZB+DBL9cW4zg-Z5|9bsf36uCj**dO9%k@s<${p;5sa!`Sm-YwtOcdmgXQPnP zz2>+0wZ+gW-l!;p7J5yC6QDp8)xomnE&LY8(B=RGp$w()X2$v$I{gvlSHGs7uUB>~ zSB@2?=Pq=zz5W6;@P#`|Q7Qhd0D`Ju{dL4}O$1i;w5<R8Vw8L_lovtbYz<dq<QfdL z6&=x>ErvFmD$m!@i(>wEvPbwQdj1I_7wgwp`MQ?JaU0{gqDan~qnah!kf+4l$jZ=S zaA%8<L;Bjc{CrP^^*$1vN20K@JY~iE>_5{{--z_JleWj%aqKN~WaTD_X_1Jmv&Qey z+Ui>mTja@ldE(v=Hsb(ei6h;|gY{XCdYfW~L-8oKH=Hf8@c|wlr^`z76V@ZT)^}mh zK75bKa@9M?qb@ST9gvGucn|9#|M3|PD3z8ZCr#_$Aw5QsUqO1HKbY%kg)gl&R+pHh z)kA5jmnQ*X^bpM#@ea{<w4OGSmcXT^TW8_)VP$!$6A#uLeqQ)D-TyD%|1Sam_{WL< z`y~A1^;6|Ia6-b8o1MtPlR7Pl+j@I72%P7R@4G7qf+x<kU-ga1Tj>1kgeyr|<DU?g zZo?@Wh7FJ0c>=+w%}$<*HUSzhYWc#9D1D`^2W!tF&K(z9=1a$}W#5^_k5$J##;nMx zoK<2uG!0JO7>^dkz@vafKn!WH3jHVMV%U&tPHYX4Wo`R&R_-s}`)vbEbrrce3tk>- zfmrovg90iy@UllA?Tup{BB_fo?SG&IrT<+VpVJbdrAF<)_nj-{<pZNWH!C<24I=~1 z(<TW66T~RKf?tSl_-!jdx)}!MvFHSO?#4)NsL>*kS_*Pa)Z#t8Us;sIWlu2ABSa+t zR_?!wE|ExqjqP9sw!C#mqT;q9f)c8HDZnf)8~8IY=Ev@*f<M6{TdqKGw;3n$^MGRu zh^kz;97X5R-f>L!eMcncP)N^xg4t#_h{$u+<UJj6EG8UO%kre^L_bH4l0Wl7DCj?X z5INQH0~-uxc_cnnJ0mWhf|hw^+=48>dpMHoy~n@=bAXI4mIC_n)LhSPB&Bh9MtGAt zI(Kj~g{pH>{}3N@aa1hv@&{~sh{gMIsI&f`VfR25n>o9v9Qn1s3~-zUmflcTuhNmN zggj-Xm>zhSuR<6gSSl8xrZF^zkRj{dV%~^<pQ4bj7c@;MHePJqIW|j(PaN!)o%+hC z$84@>kK<-9?+E4P`M%vj2ZP#zdV*ZZ)!sD;j=u@p*n_NVP5cP?o`z)!K=f=zU~_43 z(9GFlI&_n&HOO-nLM2X$IRh0reL1;(hSjlc;?5#hh!~Nv-4z+GVzI$`tB2Dp!Unw0 zUNafA=zAWh1+wO%y9{C?DFD$11t74fpsQM}fB^;dEi_B}%+MaAc?qe!=_&>yM}d%4 zo~T=7pF=3HZx^-!5IgH*9<>Wbxm!GsA-#Vz{%Hq+GGN>T{57geOkZA+BkCvWvOEMu z>gUI!7w_F;kn!+Xk}zgsZY`RMIqNt)>W-9J->bJpN?nI9z;Jx9)dLe43duyL7`4Z1 z(6AkT6Ery;rcHEMHmgc!0>c4w6jD_izjN|K1xO@1Z`ba2D5zCd`@U_KwY$hZD9fL| zi+gyI!;Rd`!fGVQ#`cjf?a$mM1-4wV?MZChrZ0TEN(~|R>0k~oV=pXaudQY~%BhR2 zi|ua|J9`naJ&=f9Y~OB?*$_V|kUTX&!M|YiWnNovHY8Nw#dE^;V@lY7;rSMRGw#-! z*ET#atzi4wq;1kk0sjsFkcgro=lw`}Dh=|}6HrBnwg_QkZ}`>iPSrr#kCni8NV(BL z4Y$_o8f9XkVB<Ah&Cae6u~|D+V|9pVE%b^sD|M$Um83`MQ`Ik5oku9OO6}=Nt@}vz z>E+5ZLalbjzJsvn!tSitQ4%3{vl<{2!q{iI*4nMT@D+QW_sGW=VjXs_c=M6!eS2ZW zN0@<x>Pkux(8^-8GA7>nQz6>d{-F{dg4?jgB;c)WZ3A*gd{i^5Su7`<vQCw?Ts6jT z)D<iXl@w};3RYKYNhf|_>juk0)MmF_v7?Rl^HAd=IRbNAmusuLwP@m2dF7M{@*l|C z9b1cEoaJ~;rF(5H#g%>^kNnV3c<~S#3eRA8hu?9P7sht%RNy<~4hC=d)vu+25h{55 z!A-oWTJhStJ~QPdvoX1#+W<SD+84ho*T#q|3jlS}PZGl~8#_(_#cSTPxrA7OrIFL` z${B!2o3jr7xGs2oVzY=+e_LV>c>s~<=aI9GJ0Ei}1CaXW3;@OMR<{y<?k+qSLIwy@ zbL^u*l3{-WWWcjdXk+?H4&8MyTkL!^6jluXc+?9F9%`0D&ARebt$7x1i-YT{-{zbI zXv#z=%gQNI8>cZnb2DEEEo-!A!_AsFxEHysFOtZHgRGa0u3)!}vYOF6jW;j4B5zB1 zGB+OUj!7u<0edT9Q%w9KahXqBoEK$TCN9pKX3Nvt^0c-*ohvWWl{dwaXK>^hsX*KI z@eFf>ansuPJF&&fWW{-*#d%@yyvdsi)){hD>|1{X8Wu1F9m}M4^UdOA&mp?D8L?XO zoSt3d0Odm{Wm;K8H~uMKSvJ~Gc@q$$k5lB@JV_f<oZDZV*Ke49+r}c8vf+(!8`oza zzdO0#kb)ZjC`tKI@)Su3KqIB8kVckrE`(h!Wh_jer9f^eP}o<)n~L_||4-~~4dcF! z6wVCDF{vD0z4{~P!q>+{kIl3wDV?o3#HQ+<)N26e0JB0pwnr7d?s7ea0Mj42>Zg^> z0`kd5^iRcD3vXsgeH%Q#9PgA=b(<B@=cKkzqCE?4?%D}UPD219F-DJJ`PpEoYf%-Y z(BK9xQAAUA$$gP2TX<AgDVIA$>hNbdSD5&e&VJ!e$(1RWbWvAJ{$q+QOl%1gSv#_C zbu(g{p#HA2<dF#Uv!qGYd<duqHPbnZNKHib4V<E>e&se(oxW2AS&X&$9{^Z-T>$Co zoC}X|brKR#Rp*|+rBi|vZdG?((zS9H>SgB{Aq*0<bt*_!=&_)D$42}2F^fbUu=gUT zYC08LNmX}x67|wd2Q^Ac@FvAyTS`~nE7_p4>546*3XMe(?N+c^O)8iXE3c}o87lba zfv>r?j9;R$wSJoiI9riFq7d?)vsHVxh_2qXwau&9A_6QHWt+5qv-Y5LLx#3617T!| zWVM2bY>KJCogretoq$yV;)#nS1MEZzea1K2E~kMHbS~E#&QB7Z8gOP)QYZc~c4rTf zdFQ93{w5oRT-pd6Jp4aX4(vPpAE<HhyI2LkfmQIP)}3j<^y{Jf*e0H8wG_c1mlWW> zc<m1R)jPQ8z=rm0FD}y8)&cv+7ACvj<vHzB7pN42>hFTxgA?kJU_7OCAxvyrE^~yz zCgRBB!Vv|@>|v76ZT%tYdvpq%ukA~ONVr?RqhV&IzC*^^6dN6ijg69aKafe2MHanl z!LbY`{ZA9A&Ksv1W;K|ccQDG1Fxf-v=$T!@x$@L*XKL?1PSrS;%Lp63K4^G4uqNxj z=79OXdK*1LiqMa<$g5ik9nPRm1>j7oy4{Lsw89cwo5h2o;QG!+Fy#Z;us61>t%Xer zInh+)%+L-bRbwQiJIbGVF5m)T-S3|~eNK5kLwhzUzsXlW=u|)Vd6TL>ExX3Vp8u7w zdb6_TXF#e@Rc9z`SE7ITdE4YHN%+Cg3(bkGIpX+ECco3CNvstLk_%A3IaTPU{0}K_ zT`nMBu4d=}N-Cr&o93s`#igf<3-)GoaT&75vr{-S$HqlCtiE${-iDb(*8exBeobM^ zivS<NZwT&`95ONb={5BeiqTKU)nCE5;>gK$W-dH@U3?2rOB{I-G{0z5*iBdslSEhz zN83V0>V2FeRBZaA&*X+k!E{d0BH@#Hf^XWmP}7mSeNfeN;gm0VF3|MFkFS;dYx`Z) z$&m*;&8SxKIXW8~7}k<tCW`u&U(`2-?K)+gBCkx4GzYS#A><FL;Q0jq<JPy@LS+ZZ zu}tAXxn&u(|B)Mf6!LSOc$H3_FIp)fY^iyND)pr?%7`7Yz=LmgaHk47x7L`Uo$zG+ z(%`-YMwXAiIP-U(iKTE&HT)M<akt1{MMGmfZwjGUp$1CqrwF^?5lPUsD|Lpy0GN&> zQOjxC2V(oDx6lpf0|S8WqaZ_$9^3o%-O*54$r4|Q8rIyV7@>tzhp(5DH^=Uzdh^sX zfB15;lk9?<1L)dBz~s`t+_ewc*pK?`J;(%oG0d>;6!Ymzzc_s+tTKh56EsQoBR+e# zzb7<Fo_BnnF5K{EOfqQrR=ik_8S84kOH$mW3^toi1%M!Vi_>!-kmq2(4GYy$uDjJ^ zivjL?2l`=-$rb|?oHtsrcMcgkV#rUt%MjYqa~);}$hTRro7vG}zarSGSUia=YSB|R zXF)b(669BaT?FWpXLWQkOsL9JxT8gl^lh8?@pmj0^#i}Ft9^iT!wA7MS4G1Dq38CE z&TN-jdhQAd91*v?gXgvi%lNUzcXkIY4#J(cVo!D|M;9U2fLXoHB1<Ha`d9XBD13Qv zbEQIN6RRavZ0ScQgm&|tl1%QBurlxon_7ZAH9u_r3Daj4^zTa{OR@gaEMoOhon*bF zV>)+K*Pf@{2qXy5ew{r*KcI6Cnw=m58^uO}O)a}WXy5KTkf``V-4izolJsA3c!)gv zgaZ7!N@Q(hMBd*e93iB%vjarwQq%(&r+G_cCXo<HOJAKiGr~`LUSnrI8Zfg<YF+At z2@+3E46oclT^#Bk$<XF5;cIeKGH^q;a|5kK*N2W08R{olFO6F-0zo>Rr@fqJkt9`` z(pFv^zfB@{2UYLfQ~mQj-^mVmY~nBk)%)OH%f(#<N5UBUhA1)x9}~r@izjYd<|VD` zz&!TbO;%1xS$UB}wTbM%h$2yDIcDz;m8mw$6~;8G$OA=F$(;aAWPcaaw+6>d;7Gw5 zLP0({)qJ9%3UlH_O=IG}A($qfrnK!E4m{TwK#18S$pCXz0^6lk+1X*u)7Fn~yHgG% zp5NT*T?3uPFxE^OhV@K<<GJ3dHu#lzUZLw89{&|q5|#S)KkE1IvCN~^Vs$m<3Z5r! z@Kzj1otWH$D&8a%w#;2YPfd;G14ZtN-NR{I8Gmx2`xu>o6jrvlg0jtZ&1N2!>&L)h zDL~ZIboFOekOJC6NsE>YbR<iM<Js}FGbwOatEph`z}(ARKco`eRiWFij9^@A%2_zB zw#b3NS_O3R6V3IfIp_D2@MxDCF2JpZ-vrLq(1QzVk)ee>80qp_kN5xN(LR9D?F1~# z=hn3B1{1rXCi{Gy5RAf(v>z!wS&2PDyuwEfU(X{<q4<oiEuil+-$VLdBJ2CP&t~1y zpLve7_MM)zb0(7P6BN&6K35@1F^iHmJ(*G&!GWcH<$jB&?&hx2U9&t-MQ!ouQaISC z(zw;9B%iNMb2Lvh^Cx^;;=8V3Y?n&M5nun~fav)TTyH9txfr$EdJpYz<)}Ts;7Qoh z(O_B0S6s<E2(%0$tvTTNOkiQu640@yC>_gD$Nlg~POv0BSZXiv@hqXfA)SMbY;y(& z+n4sEduoZm@GB@J!aVtRVR7in0TS`d-$JyVFNVrYEqPu#c1UGC>;xGG#Bb_hgbGG= zJkO(i$`dDgGVWdgw`uj+mV|EK=U4s*VM0MbP#3_<2E6m59aF`YsUq)^2ybAhIWY80 zK(#-<I*|Go1&vXR{^L!-l_5cnz)+G2vw{)EzD3Z-2>AOs$s_1HUZ%&>&@}9nnGQD8 zZibBhJ2_?pZg8lXv_PZVpZx}$Mm)3m2IXSo^3&c|4GDL@mtXVO_NlPR+S2Ti$N4|f zgAoE<V<tTWstpPwrG|}s$!IIa3z)nN4%LF8R@z}1cj<|Qb{L;O-rJZd7}D^n5XpLx z=e`fuc!F?`JGADUa7k4NeYsu)19e;^%dJPXwj2P4`t!CiNFkqBCBZIv(}%m{g!-Mz zy#PSRTBlBlOt{1P*Y+-@WAYoK&Q9TqtrHF|{6Mhe+o|)ueTv9U*PxEU6ETA0Ju9-` z@Zq`;-eP&-z%MgYSRWiXwBil*if>^#3q}6d{=`Gx!u?uH9;&iJA@W?N-<0iF_TBXn zrtl4AS+`c(xd%A_M4X-@n^Rz;0DR8bF*u>k;5>rs&5v>iSO}QrcxJ``2KX_~?(^0P z1onm-q;yUaA)enq-2d<e<#SAr(sYmF%02@}6Dps{7EtnlC^3w%f?;r;5LBwtW4Bdr zR&7|W#jB0)vSrJ)<ukbr5Cdc=!A7UA-G;uSHV8e!hIiQ-e(}IQh|Hm@SZH{J=8X5v zvuJ&vGfa;GRxu6?IgBVB(4={)tPCwYY3#`{ZNfJ?OgFs4Gr5l0T=gN<C0%3M14__# z2ND42Dmxg~dRRO9Y&9NPo~^;JE6I#sti=%-*!@pswr9;0X4^lOvmjAMdoVhdP_a2w zM8`1G!G+FA_8h>mTAjX39A2du`#^QvCK}VL;8?0(CYovCNB~TiA<Jk8WaBy|aj?El zp&JurXfI@Ps6=&}a2V#ueA(v#1P77^4?BXwN#>j+`wst_q_ZMaR9q~`2^Hro^*_U; zAGJQP1*t28;izz%q?o6P9n(Z(b*k;}ipKV<EI${W{z6N0A>{n`i1(?s|C*ooBl5n7 zF<!9l(pfbL4qpTDGCfM7jl)<>0rU(hStr_SW6&D?YDxNPsejx})epy=uaktYll0%} zH1V)b_eivPn$$5(@-KCg&^qxm$X_SZul`SUlKfbwavDQI@CQ|UAtJ*mvg&V`tz&yT z8F7MOwyL;@%f|Ks`DkUNq$j{-J-;GZ*aMv<`A-Mzv#9h^`!ERU8%4!$;e2_{<jza< z*TMc0l2C!=ZivXE$69XeiZor1B&AWNvi2K>OObP79RI24C<WZlOSH+wka}jeItru} zOPT$xohpMEa~{|%(x}PoUYN}8QwGq~{+vgcNxAb!IUmJ*#5Kbw@$P#EEzyjb6F8uC znKiXCD#{TcZezdbb}6L$yInZ~R{-bA6Bq<Bv{LkQUap-2cxxJ90%N4ZH8XjQ)i)Pl z9$_F{+oPs{?15@dl0EGD_P;!UsGjE2jJUv&Mnx;4446KBfGOb52cNYu=A2LvJ<rk` zSoZd%H!xel@e_q683JTivD8t_I&$!f$j@q!DqFG0Qq05A(7Ifcu`uEq%W1d83P=CC z>R9F+$KZrxt<V*;YNjT5>|FambpPXOgcf!M2)+-PdEh(!5<z;N-~>;@+YdOKqEBE0 z5t#D?ctH;oM?<wX?LTAvHsHKz^%cuXH=Qay5|&W!Xg4dEdQ*G4m_d}=nX|}i;v;We z)#nRr3-P&GA}}u$=;z^5zn~O}`KNsU$yf*bbZR&K+&u6%^(AqV>lC1&pB{dkOZ{Ve zOO)Q~JC^y3YjEOJl$U+l!LCYuzr7_gE-E<n6Br>L&x{1<i-QP8NZkJKyo&{?ziHRc z7aWby=ROS`_<m=T#*FOi+K)!y>(Kx~s;_<YDG0Tmwk!tXG}6pTb}hudMypVJ9)<~L zqipQc@Lu9y`srnwj72T*jhS7U3Y@V8f*khgoZOXs5`SMi{xpHUK9=zW`uapDIvJh+ z|Dm6;HBe#=S-Q*WgW3utX(K9;twa!|oa~5I8#R3zxn_Z;&P)L#LT$auie60_menI= zh}-fAQ&vHVyk4?uG%muC)a-aB0;tTehJnFsf3URqan6)PbGAuV_DAMy*x3%-Mf1Fs zwroLvq|q!4jo+9qjM%WRJx8d_Ss+Ybz$8pDFJKYxv5iapK&y3WO|vIm+YXBL)U(27 z_4Uy`t483p`YvGV`C~mvp`UpYnvo(wF|VZAO{iW*UkICan159WY=KY1rhP2YsotbG z-`Y;jY}ZIFa-kE){Vg)RFch%f+HR-%ae4L}#3iiw*taV~u}wWo>If_y;co9I@b-l@ zq#Sk(x1%0E|86_JHOtxlrf=gKFKv<wHZ2z14wpAZp8vePE_!EEQ}nH0DZK><N8&CJ zS^`&E3IvWND}jLu(!Xlx^7kT3k-)DW;sZ`aWtjgoQPMfy_CKDZUin}8z%i{F5sSa_ z;kNd3%8=T_Z}`97Eus+rm=~Il)Bkk8Xc}9?OF#**QF%74F5YrjfSt42ux;K4%7(d| z%@$w3$J!`!HNR+u%MhqSTxQHqACK$4PN_`XoTxd^$MsyK*Q3_saZe~UC*w|@p|`Z0 zhkq!K&Ql&29oKaF&^;N?Fj<S?iyW#+{X@G=Ay^R^l1fKhNc6kvEDF|=yMZPK2_yhR zw6Lp;a@0Ujc*3cE7H0pqQWM%M7&bmwy-3m~G}vw)Nf%iWDn}?#Hf!ylF-Ow56glqN zgr>g>9^M>FpSdCwm=hEO53_1TWc&)X0e)4zIEQ^k<8eW;1P<^chawuTtt7fXN>Y)# zLGMD4E&Rui)u(^o=l=LsN!z&n_cx2P1pr{>c^4Zaz{_VMlZYr=#`bZ7_6(<)2+TQx zRT?-y;Po?+L0N;8G<@)NDqa6*3vD0zgj4`r_BG8@TPM&fBs3=%6aY}2WF^TYNse5p zEmvvIMOf>I(KX@j+^4;{1+LsoS8kC7P9JjeE-`Bw&AG+;+y#!@97k@pEqA3Y_eERo zY)fvcC3h7d@GZGnmfT!R?tDw`LQ8I;C3mSMca|mh@m`)|awRvr)<D9<*9A2IVM5M( zBa#f;$JJc|keyTt8Yrx6jr08H_<>UY1FQi|>$v?Q<CF;F4zR!-6eKET40!yXJTQ%2 zr}+syKk)zkbKqgVh4z~Nk#B~ndc6D|^IjwnGom=;k6p@NCOOyN72W#;2Wv7uJeWY> z7V~AyePFoaFT;Kr|Bb<s2LJyj?3d_sqlKx|f8)8xr?EvzJ!3jRnkH&(>M4dX-3auM zTKii%-gJkm`2VK+CbB30dyJ(mN^H3LV_MVq{<I#;k7*6VNZXV@O2@sNQY#_@Wy#5w z>azvu*&O*#GJj!Bajz64CEd#WsOtqyIVkLp47UgcASq_K&@JTlHcHWrfE!SE%WQ$A zx;b2b<WJl}gtg-G8FU|3MT(+7GWmQLwjm+b#OICQ-k!qr|K8&gO5F;98=L<^#$NC9 zFYxbqMXfDx6|(4$48&Jng%EjsJ}t0+-<Us53a?>C0cHi%+_xiw`lo^TQKklO==uVc z*zrb#MJTl_Wc)ASl8#qo*BiYk)m8B0Q+~c|y#+u*%y<%(&b=sD-dwgk73%;3@?tI% zfqv5SUQ8|7R-Bi~PY28cq;5uPh&VDW@)rf&%~Uj1@Z$CiS~d^d{V8j|;=@@&4lF3s zIsbkG`z(wSVG_W|<7jx69}J?f**1F4=tw|s4r};Ica%#IC5Q*O^uw<mdr#T_L8$rg zh5lJeGNnmGO+HB1QEKP`+b{xUu_r=zROCK9daO%?P$KtEfcrHMaHofyr^Njqh-ZuG z7Xtc&Iz!ue%R$n9@;~fL_;+cbq5oI*1LQXOf^?Rn$(OY;Gxi2YZ4QnwT1s<VH`Tuu zJ6tzF%Z^x-#o`z8g#v`hd?9Li)Q67z7Y?jRH%>`SeROX~$mS5GaZl>@kZj|j;v*p; z`uykMaxQ)W|C91^@?W4GYvym^mO^i7URQkri~m`Z<>52n^4t8+T5S)X{R8`j|5-N* zrCCvWWGoIaR>+N;Bt|n2bRgL3<=aX=Q>P1!Yl!iX%=nV{nGoJig1$4pBr+aip4oGi z-uUyF^Yu&zsmbE1&D5o-7%okE3U1(cQqXL|{|XF=8-FhR;hz7)GwjvSQ$BzTJKz=l zoQ0;=0nx_%1?3u<rB|5^FR0#7u<I4Q5nwH_#>#km3h)+%k6Nu6R!aycxlF(=w3Mso zONR#Y4FWy}bfw{|z9|BSQ0d4GZBv|yTeM`HgP{f4*-j%*Ks`VbElauf{bSys7;K2= ze=vJ<L#~VfAkD^x#K$d~U%k`p$PL`7FU}E;b0gw?zT&51M?*O50zf(*UCNd5>xgGO z1pm&5pisnj+|R=>Q*pm7H>3}t5nBz~JGR_V(2)MfzaR~24)>Zf)V6Yjy`?XM^Nlv$ zTAg9Fg>bHBfuw3lPHvDpS6Q4J*1waf{RynMiOeRPr7jJImgas4)zU<VjSfCgFHw^` z64oSalblc)F8VrE_}p#v&H3T>vA~*auDTYZf-#}KCZnKWVQy8#HS{YRIQ}p!B#riD zQAH|z>Nn-KT$N?1Xf!w2y;N+0Bi<^x`eTLSu(~S1{V`(m29hbLpe6vg<hG@p*E&}H zrOJ6(WnlxIBUDjF-h^5VU?yXCl~8Msy}bu+U5J;Ut`LJ6v{XHbUl^e~601kqU}v|s z@@J8wA!5>Yu_qXB9*Fes`m{+(eN{CdnH7&5Rn@yi^oANDDeSkyt;aZPV`$$;zR_Hj zd#T8l8*EuB?gPrX4D}L!NhO(<s=R!UkHivDld$iEuTiMpal;0v$U(W{Y{|%V(o_p0 zC=yCvBSOk0q57&+F;ViAmn^_yf(bu;{+_Q+sE&|Y1|~{QjFDA<u_ZJ3{OmsRv(r#r zzW#0PdjMHIz0+ATaHa34PxBwA_c=?ZToE7fX<ncSUc5}>M)3(cFV!Mu%U@^$*(C%& zApb!V{QRQ$0KG&GE#E*7MP8r@?s#f{=OYPyC}&44VA;uR@gd6EVV>ZudkltN7)Khm zAP~6DXf#L`_}O^s!yXSMF7TrS2Zh!_EAXBz8t!{xeKjXpukxe({gm^cJSp*akU-B$ zK)ccHfZij2BE}hU(W2~lmQKb^)`JYt%r;vcl92ha&Tg~W>g&n_T)9IM_)h_xxgZt- zr1;PTReZYN(C?k7nRCT`Lcbp8_@TtCrB<6kj4-Yy-xTe(_Na`;i1nuRQiwvEBJe3T z4*z$@WB$8E<@k8U`qK3U6?tJ5d1_mt)K2b!>gGTt6;4-0{~H^~?$6)Y)IuxaStP7j z!Bxx)s+gy&SRtxdAqM7Z&b+{!c`98Dn=>!ip%>)L3w=+KV})g?AnUjMlH7;D8OBvW z?o=5U1X7r-mygf*+7o@75;G`S_FeB}CZ7)0^1=|qe>0d(0-Ge%eto>}JU<rMShLya zBeOLt-pYe503;>IuDOoNx|qOD_MzKhFJ+XXo!wR*Y^b#xIQalRA}4|YG)Ug1YRWu; z%__G)=gW>^JGu7|5E4~|MG587{Z$P!&JH#%2m&q{9FUKvkJ^L%b!80o*AZz?&p$oI z*A?`fyp^qIFWh5#0cDNt%01Sp0IS<RQtb|i2Fk?wxz_X;fpK4gG16$<8M7i10BPL$ zHfl+QjdHNnHu$>7u1!s>PtwEKlqc|B^3s@JfP2pow8a=ToA_+xd1F-hZJI@tIdJ;i zv65Hr8Fxxoz)i4#sVsfhb*O{7!67Q+b92Wkr+i88zrlUwK9?<sx*1wqt<|koi(J_f ziKy}Qt<J1gWorw5QujHt;H5Wf-&{F5{dT0tF*siG?oC&7R^R{P&vp<#hzPex-Xq-j zfZ2NMAd}2ArL});P5qm7!-&-t&8-iNBU6v;3bOuGOCGrsSiG7eVK5<qF42R)_QFMS zOigD!qQ%$G-@~gKD`QToV_}B?cwcbX<W$koatNdS)Av{mlV2=A@PQ3^R)lbtth;36 z0_~TPPl|k9XpN4w{?1uQ>$@EN?dAe)r1bR2n3Kod<|(}aosR<t;(Fk;(TcdJ{87U# zkM56_Lj!tLXPmvtxS*tL)Tv&g7%#u;Erj{iEvNc*x%d$*`ZCBBIOU0rg#dTWHto`O zZakB1{lxtFN5a8RP?L@v?%D)1SX)@99G#}z5el!<pDR5KLYb@!?fb$fz&ya%o*$}r zX{7!UcD6cUF$Z(G*R(xCtMi7}y0-tM!F+gY|Ey_^I*aV@$7YLcR-vt2^h~fe$yEsJ z)ZgWhl(Xe9oVuhN4g5QqcIycy|8KrKMTmn(;&+r=YyvZ<bcJ$N>k2EC>qGbM;u?$8 zTe$W=S+(ytX~jwYdp?qUsvJQ8;sGUkDJ`x`mLhjc^oY(BJyw_Il>~a{<7$_IvJ_jr z773Qb1nej*R8~WFI=DcRa{8Q>cJVUKu_za^?6C=OsZ&Si2_NzxMIeT(StNL#k*v$x zs7LfWj9Ouy)q+vGET0_cZ;_e~Alz}2k_iU%OsEs#Ge(>gChki_ZTT!EV3Z3S%@-h0 z%tQW02nYIx2h5_yi-l>qTt_^M&9gIUJZNRex|mlo>T-AFZJDt?c2(#sb~dM31CuKl zT;-I9+|6@wF~wZ$s%wSIL5$7A_n7+S*3mH0CK1qvT%s^@K$dF(<V_Rm@x`#gl$y&` zXFJ?s@Vu=aQL5XPX<jq;-%$_br|xyC*XNu2FRP{b2=*Wf-c%lDK6OVuKhJ#XiuzLS z=^fB(<u*Y6JVmCST|(wyU&x$8hzoIMV+a2P8<|a_VCM^}(J=9b>KV?fstqr5+Rdu^ z&C1jDT=iycO&+HOori(lRpo}OTIFlP;X=0N8Ur5@7(zfqx}ix67+B{<9h9@sot@t- z#J3hvD85}Jtv@?P^Z1Ibr#3UHP357$@cY8&7AZG0DL4L%MZ_BD<<P&II1TuK<N$n* z|89|*T3IY*m56;c0+>GS*_tztY|@3DuH|+OKrEjA{>jw2%Gr-pH)&4{U%t@PYqp|? zf*scAH2*<%&6_q+h$;Ej1)$RDLK*|l)F5=ZYzui)Et>&!iY_Qh%a0Z{ZR{;$t<{IC zN3_~ER2XcRF0^_`Tk|C6Dh~y#Q13j9Ti2T1Ak^j~J{QR%QuT8)&9T%>$h@ZB5}V+P zjqjoG7;Q3*+_I*@tP2?kB#ItxCxS_Kh~cfS9NVD;+;vSQVi(@tAsKp=$e?Evs9&7} zOyuTb_MoDA-yU;p44-48EjAVxwlz)WSOfoLpDotNKiS``VfE$e8iea)Ko#+<+P00V zjn?d{OV;buo7YW)y(cX7VDNclisEdI;oOZgHE2KFOd}hXvtl7&p%6&KEK&|o+y3ey zCx8!O3|&2>tchjEh7d(f={yC?OEh>#?=nOsp+-&Zbwcz@^=_?s%}$pnxbmj~u)cFx zgY0VBx8%*r6-jN3vO{ayC&0Z0R>f}~ONiL8;bpS8Mdb?PtcOEwgA=f)IsN(`b><v5 z?Vm4(&Lp4S&6UibGD^0npGdltCV4H)9LC+Q3&QkoSc#aBRs;-5tFP2v`zgCsK1a5) z168RT9$nLFmeN|eby%9zlnWz=_%nE7iK%syEg%bu5%q3uO+M%1I2$J#MTOcBs%!&7 zWwol%HcaF5m0D<{$Ix1eM(JKti~p`Qzp?3p?Qh>+q-c1AyC9~8)U06(?`k(#XL#53 z0vcL?@6XW4q8BRJ_5tDfA?1Z~7DhxH{(Caw=!FV)!;NexV=lz8V1S{IadA_vFqNHZ zL`xc{Dn{xLazKvh?-70)pgzH&h-S!GVyw6@uc5ycYpC7Uncnyd`ZWi2tJzU5+RJUp zf~A>Hg%|CJ@d6$?81On^qY-_lqH4-OnyU&Slg=M#w<OSEwXCr=@UV*y^QyaGUURT! zHS6V~^~lSovg-M~^h&7BwTKoIOu;Xa*ZL_fX}0yq>rxt-UrqbJw#he1I8KD%Gy<v` z()Y~-gb~~{_0CV=?0k;_9Iyj|+Sm-Z{6kca7?-2H2y;ZmxGn@K6!|8nU}t$tk*oQ} zkvYO$?_#8}El}*}<y_;&+NnN2hPD~uan=R80m>K<2p}yPf-_NqXDO#7pw=}3kY1N4 z1QfP<$lRGU*j@K*$^8C-UzObNbA*YIr$)B7j>*pvoUIvqR|Sxnqs_HhG~S7u=v+jL z$Su_1>oxz#V@CA74(?e(=AP{!;uFK8oCcQ3^<EW_=VatHcX@Er1<(LO&`D6*B2-Q< ztN~&3heOQe8nI_$w0~T^<oio$wS_*Lj$hp}YvUeq0Qi{U5!M|b06)AvdMTMBJ~UA? zLV;p=Nm>c|@ojXWg{S0amD_hn$rLm(UkDK|CGvb<XvqBSJA%~T-_*PY4HZn=c65mA zCdlljj@s%8!LSYav&g?1p(J<9$ejG06E!vz!J>%zIwy>Gi6;L)C(siI6-~{gtFzhg zlPQw}(IY?6pdS1HW@>WrKyw!Y8UldsIo<FC({jxfE$M848S!d~>D=|n^lEuWON0H% zho9#=C$J9PQeE|kkOK3MSkD}EM>Ds88a;nIVe%zDGLx*gln3{5J~OxFOsu+7)Bv1s zS-$#|*enXG5Ggb3epR>qolI1l1}B5*u{)Oe=xhxrtTbPJ21cHIqNb;t_P!HIG?{Zg zl|Q>}yHQAzvCU#j6c<lCp3zr05tUK<)Pz9tniX9v#Qz?m0qe+bZS$GF`AAedZvuRp zI3L|j<^CfSC=sUq^H~~1D9_9xDpRa^U|hW}v$=|O90vH`wG%7^q2-8(v&z~qk1Jbl z#k?vPu^=>P1g0yYgulX=gI`zj+v7oHWiv2A$oSX`doGl)w~htW%>VI3jZz_~de>fg z1K-+YSpzHxp}IhiQw?GLhZ^<cv4a<(&?y?9Zsye0brc=C<Puk_q+fn(;lj?Q0UJFp z%v5osnUCo}b4WB07M{_KX(R{~_f8SOZJLy?UiD6A)OR8~s#s8oJJZ!?@Af{+I$`ki z0<-r;DHLO#|AoS*yG1JZr}xx7Q%f?0#$!rsk`C)s&xxBCWNgKf=b<JMg=p@1I&WUX z{ZqIXoYI|5a~_&;w8-#xe6K}DApWLRH-<s{Ljb>!-Km3_czb&+lw2ip#Q->@u<<nW zVH%?Fj!ONNaqwbe8mfClHfwx(_>1DLU-I<}e_bnieM%bxEZgDdS?6E1P+*^$?h)QX zzH`S00tPRRPp?_UR==q<zrff6w~#4BVLh>}MQ8eC0!capUZs8`jjEETxdGkg=dxfX zm-lyZAm8QRnx49G?H=O|%JTkoLVYqEHjx99L7CJoQ*cC((cGJg5D`^?ZbaqKfnF{Q zpe}h~mjYhBikGVX7h|X?A^B8T^BhZ}{T0z?3ry7<di_#hBjQk@%lT=W1vafjda1Vr zR=ZN;b0YW9cuDj{b%>rUMj)fQB9~l)c?R<1D$tbFdCXQ}^)9U$*LVJ5<HG%JZY6_% z@1Uo_=_T)eE#B($=j9%{tA3zFcIt!5QN9&C*p>PTHCiF1t=Li2x4}1fv1PhLHvNHI zjngB0tqLeEr-~qAxih_XJ(?(JDKh<GV(5zT7~%JBrDxyGd{95!Xg)FEdaF<1FVBqf zA_8c6J!Eo{(M?{u<KZM5pDwP%hoa!lBI{w!baJ9(@GIp0Ux-y1&QA@QeI`FOAlE&S zo&U<ev}3M}<~t(&*LF-rDL=O33A6*V9Dy|C(;vvxgD~}_O;MaE8oPt0^gRZBv;End zP}Tm+c#b30kK;MrCm6ig_Y%^1&fKoe^Wl~JXi;rEF8BpQ+k)&S7Yy2x;q&r&`GjNR zY3d)O|Ex`Z6wT{lw>~~umxtZj7eez!x74csz0z`I@voJxmGOgP<_G5tqQ23WvL^rF z<TE}&UAFp_k2^|W8Le?y?>SqQF6#t-Z3{Jh3Vb?D`@<c8{`hFO|6>%&cZc};gue>d zmO(7K4O-goVhED>0`d{}09R;!-zAz5&90?eH<bY-fgTa+_XM#jU`qM0JHRS^C2qdh zwqSZGDB~9A=4$n&&^E@+>D_rcgJoi<z{?rDdBy=TnLYSnT@opfyrQ<`DvxG#fL$Z7 zT|dj73OT_W@Qf->UMpe4uRdz#rlF|!1}w5HA~z<4U~i2cRcs+**lj<*t&bjI1>PWy z<VBC412C><DtDZ<DSnK9Mlh#LJ0}LjgCDBDP`NpYF<N#hY3|th%ba?GcopIP007@i zWAbO<oT*i}C<uYA-<hNe&IlIiY*Gg#F7sejTi9LQk`#Ugw7`glZ2x^AydnH(Am2=3 z#F+w8;6iRgLO=(7qcNb{5sQKkH;rgZUcSn{u@|=00$LSLUF2Dl>;{zv5ZN)K@>EDu z%+ZXBTqRCLJ6MoR7+PLH4<l~N^dH}yzzZ{{_i{jFajKuXV#^B~5SQYN&<;1`<i5k0 zZ68_`C&Smj?N)3tMlU2{2sn_-Xkb?5^iXss-%pB%{lo{pV(hP=Yt#kz2R{@$eZcZ4 zAdg)OVlO3;6mW=_liUbR`5D%K++~iaYlnTv^R&;pgDa6<gGt|?{A`zo8XY!W^JnY= z3%1qkIyGLhb*#3Zk`BoIB-cf$yEygA5S!I!wK&zMf-!ZV=~%~mGFDo|vBTZ06}s5R zRg321pk2R!o~(=F*-c!?!n~CR;G(Bii(XwGABKZBylGp~FBK2uoWJ*^v-`>0QR#(m zTI<%GP`y{zBXa#2f#s~e_Qq*sDir`u^Sp8g;V!#bh%qh|+olB$<N)>R=Ybn0l!b2y zPlNmSX*cXro<{sLFlfc82RQW|I4uR*2Je+rNn6b6qQ^G0DyuVusk71wzqVewYRza@ ze<eOD)XXP`HN-291xo=;t9qZ+F^%(16FK)QA@}K5NXJg901gGUdr#1K$k>@b6nxtM z_#@CV1&TlAuC%tcA>PbcZG)2?4{7dY5y8ZYD*U>-+J7YRX}xQN_-O189|8i2iXd8q z(3@c<x!(V~^RJjNu0PK(JbR}G5s1v*h#(w)vGuGB8WlZut(*1Z85TZ-S~zQ_8Sv{C z+fe{Xwg+)TaFhr1W_p3l9_rUi<yd`b5OFXGY(hvvOhQINZo<-pH3=^zY)jaaa407? zFeg`)lN+3q3zf1yC)bdZYs|@|sK=lOetjCRO^el1HK<>k7NhlR(-O3GTx;Eqe`?fj zy(c0Lst@b`V1@;Hwb^142^@&KKEF0?&9wisHf><GxNq#C9<TWDXhriYV`f#g_D0p3 ztmV#UK2#$=$9uoaJe&l%<&Cb^BPTfiiq|<!>fRRoztX+I3%KYWlPzQE+)W>A&rl7B zbGR2WscL^LPgTeZ!R)tu@#0LN4o27L*+RWFo+;S3&+i?De!6g<>E&gnlP;%@<m6#J z+kVJ-x>b4Pk7}#6jK2uj3Kin6SZu)@#qesqj_J8;#!(K*Z|{$CkUt*j7J*lyCLQu~ zcg>m-9KdKSL&)4UCD!5W<2@>(KfT{MhLD7KNSLGe)oYfylT%!p74VaX%mjzf&*uaJ zfs3S{{tobYs>?`o#6bXlwS1b@Vr~-UpQme8{J?iYM2hJ+mkVCw5`>8Q4vJ_Fglfh& zw$iEovm7V3wxBJ#?0ZbS!jH$6*wmydmPK0<lCzduSBfAoE^MM=R}jVOOJnT__BrYZ zjNUXo`81VVlYsWk&aBPKy&uVo3OEwA`BA1;CD{BCM-(W%Q{|po;)p7Fbwn|;uUpZ* zcP6vBTeNw$X7Ai7n`f(z&ka1IatE9wQE}gMBpPlF_(8`6AfMSl%Q~0TCN&2-QPYx} zw{b*j3nBqqbg$^pjCDfVko<_-`$XwWI57)che^lDrt}5QU2UY|c7(7^YU723P+<7i z5P~T;++~X896g<dO?^f7ymSSOErV+z8Wa_NIM)8Os&qxM$hrNki>Sx*Qo>XQPkTjH zxSWD{zq*{h`5jkWfXP4UZ@ayCzLRE7_f>^%3E;xOr0j*{AI88)Mf)Ax$mcXt0Xh;V z^XKJe*{0QUgn=w+5j$ir4M^I*q~<1H4B5yQMQcf^s7>J|u@`v4gh4b1DaWTF+%&e0 zET8i3)ugLgIDzWyZeOFWLq=6G4#i852LZ4MW3+dcs!aiBt(Cgc66<nj)^a7zMtJg9 z9~}5-rLqNZrxq(=UKY|B<JyC02Lc@Umdd+YAznWk^WORaN!5^poLup2^rZ}Ui1hpw zC(66nBY`u_J*{`qNi?ripuTbsmbM~777m;K;wzEggIe^XDrDE+ng%Ms279JVGqWMM z5DcoE&NN!UfxYozZZ@QAh(3igfdA=t7uznku|3SELeKy59jF4!-FzSL!oBRSR#3Q6 zpd}W_41GKi_4g$aBZ?7v6Oz=G639{umS;X%@#G&X49SL3Fn&Gksx&|n6baZ)iS8a# z04K#Qy>V<wrvx@?;|$jIr8Ic)Ig*lXQ<rI*VA3=?J)^$cSq;uv+I4bVvqK0;2JFy& zv_?D94YSbOKLmN1<l|jr==YYxqfNqEFfL5;!r?bAxS9iP%{Rv0+w-Z=@UE{#bw<}I zT(k)A&K*8muDVl#!V9`vG;YO7jUw**Y1qj|rzKEHe8P&IMjP|Uyer!FyIOC~Ble%& zh9Y9cP5^^FGOtQuw*FAPQ)$k5#IX7{IKxH>3@Zzo>Jl8XlL+Vx35#RF$sGytF=R@a z-l<L4<DY9qOV~};7D}dooKFpyTA#IuPrmv~sQJ|KVwd;}HzKK>Rl4&}4*e8-%;TP| z*9q4bNL2xV;=B}SHfEf^kX?P1LUMW~A)qW59~Ul`AA5u&($n*|RfG$gpJ98%_OYpD zUFM|jdELUriUmM{=wS_x>lD9b-unL<j$?6pJQJNG;%+~$b_bP?T74Iz=Zx)!MHh21 zB7?-zB@UM*^^E(38zAI2T|*Xf(xF*){;cxi4hDW@qok|-k7f$bc-FV|2K2Efg!3#n zvu^O1qj$YQPVbYP>A-_dAzGUAB)3@J_br$eobfeck^;ACP&4!dk>mA2HciC7LXL{e zT#79(1^h0B<=?zmf+KvBE<GlvQ<0iF0|QPTv_G~7R*V(ama1pe@d~`Jf%_4Ih|spg z_o{2Mw?_tO!1SBvO`A__&t#P5p#upz&cQ{)#hubZ`#~Q{oKH&g)QOS~9N4+&0pqE( z_EsR4cIa7XSK8spp%$FTwqKiyly%wTY1TSG5$*J0VvdSK=$YC0(F?ZU$sOY0FIr9G zG?haf;uZ%BuBgtRPa7?fe=%`8Da<Pl`Qj>ldiCMc-)w*XvR5KDb0-lj5Aq6g-pR^0 z!K<1Y0edi?l*`!CJR5gX6A^wT0BblCWT{i<DZ|U{AIKXPa=dvTZ2QotcTG=UZ>DZ_ zrL8TAd#e;6U^{$6naGR1ZIi^1`~7<)nosP<Zh*FE6oz9r((@rHK!0U3Y)<UkS%1sU zIyA%cYu^GDI*{xzM+94EZ`(BsrNVOQPuEIjQ!mX<h1+_&wA-dQ=~6^v<HYU)o%8^4 zJ(FTvcv3tA6~QAH7frr>_}o);oygYdsBEE9X_JIyBLGROZC=>9V-88UU!XGdY%e|{ z(}#B9<$SI*4_nH74&t5Cyt-H`TmfQT+?Js}vqX&!@ylC=Iy;_aTVipK!8sZ`bZ+x{ zY-;X#f1k_&J&IV_5LV`$gtY*==UdZ)>*8@N_03lld_zj9DB|3=Hschl^-<F=e2Dso zaBu|GFQx3o!|J2n>vV2O7%Hz`%r)w8Bt0UV({L97iuOI(85{nrc<UF|Zsm#C@Lx@p z{3;ynFK2t7hfNq=?I&UlV>f<G+q{akzKhC|d&l=+c*uTJ*7ZhJeiyA?xBVyd`Z({O zVX?Lp@w9T5h|<c8x|n?#pI=g9D4z2T?Ti-{=*`T@nU{E1o#H+695xu?bU9DY)Ndjw z0WWxKZ~WIbX7W7tP2}eeX0oHdiTZ@M5ySrWZv_Ac!#r?Ly^hxEOS00OE4Dv&9YW-e z7#bt1Q}N!81K$pah#W3KREK?ls@rD=8G&m_?aPns{|0l4$dBx2?4R6+w1tXb6udX* zI(9%*fbQJl2$7U%xP`rLp73J*0lh9T`Ht#*+nzUuKe^y3_O<BFq;;w^Aj0-lJ|rg~ z+zM;A6ZFKfR6+|#axIm#DGd4tmqgb<R<E9!4jK5_>9z1F1yOY5icLw^$uw{7G(-RW zMiKltDdh#(-H~8O-~;2({r(|O(QuFHKp(rt>J-#*v(77x#(`oys6o$W`syJalv(C? zV<0Xmyz7z&uet+Idad{1DDGZzvQx2d)*!zIthf17Kiyt`!Ea-xtq=KRth<+iMTR*A zq7IVYLNr5NtqyBT7eu!nCD?RQoSK3S0Q7I;U1sZeLYhv-?#SLD>|Mg96V8DX6=JhR zE8EzWlGJTu)W&JK+1j#mxoL?Hpfk*-=jE1Dw@PFZ2lMajNM+>eHpMkPYv%cp>N@NB zI;%yhtcw6Y0y*<oopLAm5h@Ya*%!d`xkZQb6nI<EI`|k;TF1O}WHv0UJo9|(bRcn6 zYXJ?0`Oh;Y_I!%RE?(F>A~Y1BBI=#tX4orrN)-Flr({&pStT>(F_p{aw6UgYpEy8Z zdkg3R!G_;X9m<3PbGiJCB=u{aAsrbFIC(6V$zLeY5a(aCWFMk5EItVwQ<L(68$jWU ze;EDvp8D$5_-t4Tv7RS<gp6QUr&y#BrW}-Qj0`m<s*IU|UQq;P4W~s`&Irf8lP-$* zAALq6vCe2oqms|`0zqvM6n~h5FzC7z=uM%U=h#&zwo*)P^b6fTGnv}2eBOZQeFx04 zXnOen>n(oLi)^Rvm&k89L?LLFI_dg1k&O5gA5?5-B>ns1{-t3nu1=xLAdqNarZa(H zoF`3>u)7L?KWv}DTEe6)kD|Y=@6f&F$cgZO>l5<kiWfxwZ{RF_(pS>;Z5wNU)<@=B z=kMw3^tr4*IRB!||G;-CZCseXL~urUC;Ql3Cf@;k!O{VVw!7V-;1vyTIMpk^P5m2X zR0nBiTcVep|C&=z9kT^=)dPdUdZVw0+Nz=N<}zS|x8AWa9Mr~es=Ork#QCx*<7?wK ztrjEfdC3=IOlAl75)M+6yf0LDijk7l;C(k@BP6lI518{;t*5VAy@AS0Q{4d!dUeB9 z<&}}75uBFKsyuo)roD9S0aK>}N`ke{xuH(E{U_jk4LaXS70>&^H;kWV2Cc70gxb&{ zx5xM_+|#?djFB>9qS#nKWq3le@5mHMr*J3<RtUgL;o$ZX&l?V4jYE7(6v@Q?#Wc!a zH4Cy!*z$XfK3hAxAvq{n;W{iObCVG@Cf1!T0ERCj3O)dS6C%bGI%o8!PzM5l;#<U7 z@R-w|n)ps{<D|waz;H)4|GT?Qtmn`J`1+O6wDIuD)R?Ug054#+-otNto+cfh5RBkl z!puk|EMrwdh@Ju#UN9YmPoV(%qx`IykxM%k!RJsE^@;nF`$^xLUV?ZxVt||Z5Kju+ zdT{jEx#wU2hVKn;hacsJ_B}Kf&L8((D}{)Os|%I9C?9%-lxm_p`@@d{O}Tk7MVaiL z=1+RiZyC(<T4~UTuqnGDOu^T5=%z#q1M~E(p#-`w*ch3E0sg7Neht^s9IG#@3j!iB zxM3R;;Ud%KRWbAS!DE85f+2pn+eey?QoPlsFO>R4;)Zzw@B1vEQqK=k%+z5IRmzU0 zP#jgPt@9A4KmRs7A;EjDxP~BR^WnKFrTMY%R7t*hwHR?lrV75n^81P4or08aN}2pD z!8=t<AB26Ofaqi*2r(4ln{ZruW?yULO#c|G%@8QAl)O!V=J+)#PG1>jU29?Naqut2 zVf^>E6C>W)cRmY7dGqBc^8(zCJXr$6EP>b{_+}?{Eh9UQ9A#>C;BYIN1sfkQ>C6-d z6N$XO>xNmL#$Nou`o*L%Qi{9`jabbYK7;+@_bh?K9hpmlJUwIiN@~!lflX=yj;q0v zcp0VIde4#v01LWn-Qcs10)%~T8H`PT&OKlW;`wM+saEM`3(uyV$|lJq!0_iWwCHo* zIq#c9ruLNYn@n&1DPLv5cYXc7%2&Su-`~UY2x(AJuogBdu2G_BvgAh5Rl}HS!<)g{ zvQnEo%Z2E1iBtRci|h#k82Svuq=F4U6JWjKr@ig~v2kt$9d$rriM=WEUFaz<8wHtG zZWJtnqg2MEK;z>I<3cHE+#bc73R_t9@hN|8VGY52zw+nj8pvl#pvBChizFVfJ%;}X zM{rvU91!-&Jvvm%@LD*^FPdQ9`@mSLGQJvUtWv<R414CU;L=KsKoRk2Pq-_Xv4z6- z|7ZB()o?_I<CokS(q*cL+w4|9CqTM9&xg;ItL68R+yTTHxj3;q6Y9`6x(&OgwJ~_= zTFMxH8BXLqtV!Wj58iQy!PyQd{>#Pom&3(h`$*=(kHAxy*3*1;Lf}y>7n4P;%imZI z%+bBm7TS?5vPtYlmfNSfMZ!_*Um>|k4y4NaCxv*`#w^6)o3Xzb4n0s0YxiEGv&<Wo zh`w8%C_crHPZ6{OVRExb>JF2bpHv^2i|8<?Q*RIjaJ$K(#Jo51+Bkdtw1s;R3x|3I zw<?nr6e}`MXenlFA}I+_>{JhM#>Ahwu)TgK9v!@|{z`(W94TvF!r4Cyr-WhG^_~E7 zH>%r~$3^9#Q8OR+x<pauG&WH<gi4pMmF#@r7Db`)Pe#$6R<jeI21E_HX=<nQ#>_e1 z+Uq{`2ls7OU(^Vn$LZ5I{x@6S9uP&j{y#f2J9`_D<+5^F6<1f~VkufGVX>={qLxr@ zX<AARsacs>UMpD7K~ySSJBgP%%fK7~rF1JV7zL}L5}tIeyd<StlXt__V`{?wKJP4a z?0kR!!0znK`@ZwM&->gj!$t<UD?!s^e@vVDmy1p3f+ptn<uJaR2kr{=a>#zkm&GPr zNeH&JUElUMc3T5W#bqX}FD<<eU3^sus`pSW<p`&MeHA6Vu(};9k0G`-3z_NJFAaOB zvFKYRC(%Lt-%d~=I$>Q|+NIl7bV<BCrF&ImNxU?rJ8B3X3DUa~8mBUvi<$#Ufr}Sc zN#(@K&X4pxlBraO1I-^il>r{6F<Q&$#7lh!_h+0RH>5GW{^g|$EQujwa&n&n3n@eR zFR$8+ptK%sbJb6XH%S~au>*muIX?=*Z45${+phqBQCHMiP8Iw;G4aW@x%maBEX($k z&m%v(k_+({g*jYv3cKV<`1^<CJWGDHTN{@(%@`dqXlP>MsI}yR&`9`@je2D&gKTfR zhUKI1<D}6MiKCX=A11conS@8xBMzv$bfv9cmC%@SWc-;_?I8Eif>ntH1q%|Z)=Ea4 zrFzC^RJo!NDRB6MwXVnYg3@Y;*jU!|XIh>sjw^7&QJ~9@)>Os0%UQ=%?YQ2^X(2MX z0nr(c^5K_N<FBqRtXKL!4@^|}9RiF!5U+XDABX|x^=T>;5ZoUXWv*Hg6>dJiE=p&9 zVJE`FTlTM2@$rogv>);CX21KV(-%q4XZIJ&kFAb6ygKn_Z|4N_sb|nfC_!kwQ-lP% z3Fc5J^4q{YlsT5C{mzB@!7M3sFoT1%MQbK%uO-4V)(kjV`m7RuAjSEPOkx`!WlQUf z4c-Xo_y{TYJiCej8ZhkbU@Wvbl~JnT;(HhT6aThAF*QfMlEcoM?_Q*FYbcX82C?3z z>S&$@jseSE$jq9IVwY%c*DsdsH(bTj$RzF2<~Xjklt0GbLOoJI&*DDKpU25CR5{{x z(`e*>oPhn?u*>lP`PR%IM4pjj5pLVb??w|WM-@IU=Bv50!IH6F_>}M5H2e7VSw9CI z=d`>FM`a~<Cg`}SHTzNCY%x;%FrC>BIL$_AyCe1nFXEmlC{5uQt2KN0aR_|(VVJkC zTPoVkx3Z1|{rL9LHH)~}e7@;WFmRyV@lC)YnlnzCPav9nKECh2YAJ`BVm?^Z6eQv8 z2iq*-0Q2rFZ&1Xy3%^;)Up7}aeG&BFo8h-<Y37whdxH6~7A$E=*2$n3Lu|U7;Z2=m zp3Uu0X0N@Jk!K!<P<lUp`|;v5F=L5&G52xM9LbMBki4=NC{}Pdr0*0`;71eb;wYXr z&C>FQB$Zh`&Zcx6n#fJM07hjV%kf1ssw1A)ji?&smKv<@cORf?%w=6HR~+Surgt0X zYq{)kt4FU|xO(B{jHFt&UUR@G^y{_DcnCbZA$d9MYy(uqQS(_2w`V_$+e0^-%{wzx zhYYnEsK6=Ef6Y@y&4A)R3JH3^56C;nl=MUypV$~#j2xtb!e5+^8HHoFaY=kEYAoZ5 zLjKb)+eSBJX8y8ns5$pB)UJcQ=&?xacep3&kiP9(Bgi5ohC`(70Cj&OHSWxaBT0=@ zu4X)(!DSR@9CS5iI2(uOH4d8JIFygc&7tPIgJ|SLV)*+e4jMYFFNxvLQ_)FlYB}rK zwy|p<GWCXw)4=k{n@i%sD)@;fUO84`<I}Bw{O3jeybBHO`dc=~)2zoxZTexMDWR6* zzZOOYs^}Rzlpb=(K`~orx7Biz=0>Ix?th3?l1qm-`cTbPtzFo|zA*kF)D-+0VT*jv zV!E^WT(N)b;+ypF#ya$+)f}h23|oitN<FLS-Gfo6wb?9BYpd6rmhhe&3Ssei>h9<0 z88_MvTs@exEoi(o^EQ<3z{hVv4?b~^i&2$qa*gQ$V2x5?maVtSA7lBgyUy%33Q^d$ z^2hN_j_d_2xpeL3%yTq47b(xX3lzP5{seT7sAuFc&_PSG$%K1@@??C$M@M#B;Z&yV z6{nV6r$uuc?e4;b@z3opG~I?3f;U)O{y3h)r|x>Cc;-#Id@8dlc<$Z_S`z(suS?G! z{(L$7v#xBm=;Ppl!BzCGBfEPy*PwoczE>4&cjr)^<~9`i@A-R44BaE&%l!lL9~6RA zPW(}{H;64NySb|{t2~6`Xhd501iNN3JJ`Xd%?_>{lGI1Rt9qKBW0g$<^)0K)pJb)! ziYU`bqwrh0@N?+5DXjd||NOQ@IurVBDr=qkKfi5|e+vEf6e~P=_iqrF8nsR+xuFUz z5T9RA#129eAu;XHLf%=1D<v1oJ7`xthX|yp5PSp~ZC%Md<<lVQ@GXL_m!#?5dpcX~ zwQ9Oo`w*0yYnHg~X?8PT;;HDU4)9|U36{$`bJe2zRZlLx{TWuYbLCI7)*GZ<5gCj8 zBmWC$u!xW=c@n0^A2AJ&{zpOu$&1BrPFgcfw=OoWd<HA(sg_lwxv5lchRX-5J!MkF zRp|#)<4hJw_C8*P{ywd~2`bM7GCH2e`<@_6ziS@YB8B5!*9?{$zUaYEvx%c@*+bG8 zDlxO~!$<$yxkh;|5xr0$rqgJSTsR#|HJf0>S|aqN>-FYUuy{ZLmVbqj#)LkoOZZrC zxtb3*KjdS$%raYgt7W3WHIWZY)cPlednf9g6ZOuCVRA2e=>`8|G`~dt)kOO3NZ*?Z z^Vs46wajU)0vN2_yb3MgN9wmWW7OfEa+QnW%>0J6-f>#bIGtsj-U$))=g`x>zdVg+ zTb$-qsL4EC4|2yZk>apcu^c5#$YQOhaNQ*E+mZjkBev>_sOGLd%O07@wgnmxyg0RH z^_-1M{P4hJ>k`k|OqX>B4XA)=$zuFj>{I#Zw)J=s82xi;S!e@cdP`gQ9J@wfS47^o z&XvKZ%O^}rNBb)}_|;ypGxULGeOa70i}qwO?kf$5pXbxXEcNMgHh-3CX?_+>xeS(s z(JnF;za-%9xP3kFFeQi4*{k>^&dIbj3}HKmNU?l>PkxE@Tk^hYX>aHIsugIhH+;Y& z*uiBmH(gTV7Hky6<1|R$ebe@~-*}bDfaKt;XrDP9N;2=F4TuJ=Arhv`LFdw8xmlc# z?uOhPgoz>t!-29$@WVtD>@o6@N%w=EIZ%6P{<`5{@;#n-0DpTDDVX5y8I+wKi5vi) zHIGG4*Fn48)dnKEE`X*2pqj@6lr_SU;Gn;`r)ZVbAz<xE({#P#b~{gUdx4(cnv@>~ zeF`6f;|FAzV_ews@>!%<<>c<)m2VkbtZ8|&>MNi~wFdP{pJ($>4eYD+gJDY>pD)~0 z@ndTG!DC*Y$MRWKdZehZRQW7cj>Wl0LJT*1@RXV4f2|tjG2;qWu3gz%{*S8K1~6V% zmG!RIpNg)^$xjX|sV<+6P7YPg(%0k^hVhS>;`o;5%4f6I=<Yq*4Er3s;eckh%Efa5 zGu^yCO{aD040fF!lKHC9E}bqj^Lhcyx<dvHx}H7c_X;{IgR14Fa#Ph{xfx40#cR$? zXOF86*z8c19<3Q27_CL3EsE9xOGfK_Pe*v-Y5asp;+oN0M)%*M8fdVLMg=tBpntSV zKCdd)*tOiM(b?G-N7t(5b1FEwYt=K>Hy}~UdQMe56W^;e_>RuVuF)z5H6WB#2Cq(M z?F@IomOgr$GM|66mQ46egcwER2e@X^$%Hkkf%?#d$8A1L=vU##^sed7sN7C69h#3| zx-+T~8fC&-=C2c?EyU?R7dmRj=mz)bhOfzTTU7@%E;Bu7eZz+LH*8pEi2?ji;hG26 z_B7P(K(B3M6GgSsCyW-dS<RN!7U2h#dpRaW`2gvp3jW0DtQ#=^$9wzissYBdX%V7< zODwb1!CguU)I#$-j)Iw(?3k)d<J55*ir36xSAni+SFLhtb+u?2h<;mz;Y{feM8+fv z->I_e236T+>}g0Fk4h*i4DhWofc2;<dx+ah-gijDjFtxPC|W-9KM)?(MXCsGb<rG7 zSMvJetUXDlS$a`t;F*c~&zo~m50+LrX%+^4GWlewkRESQa&9*1^W)=p<$F!)O)2n| zIMhuvb==`Mu_Ys?UhzBFhu1bbxgIVPyXoep6A*b}KOv&fJ&8)oNr*zo)d?vE?Woq@ z(}i)!jp_gQM!ET}8+%kWwLDp-@14iCpiC2nH!TjzZj;x<*6Uj~9e0#WuWFJjZ~}_y zS_5#XstNb((xdDwp%<*<;+-P2$R_U$8k2)}9&Kk0Jep`h8HkSW*Eg}>P=($kjY*oE zBoIn5kefXQrYUk^AeVJJID2x^n1LL;NTD$bJIBbNcl3qnk*DoriOe~(lZNqDev!+4 zlh5|JXYl-lFrSU_**LF_^4L_Ojds~sr%jz}(}2<ubHI(|%Iv+LW~Zz8cV7R<7*JDb zEJOMb3_@&@ol3AM)C=P$schf?>M5V5(dfaEJjBW9fGG-!;0VK1{suLrl8;<7limIT zE9z8E9c8FNab~o&8K>IWCwy4S^HnO8rL9s4e~0{DK9}9xr^m1wn(K?0Cu;yD#n2OE zc@SpAxugf$`{bS%*~XV}Thy@R$w`&cxOhl7znUQ0t^5rkMQev*s5a#}*!=O_OL)$@ zmOLlmp{Fr+(?E<Zycxs=vd_XA{KzMp|KKMcQb|n!-;rn8V{0#A{$;`PXO(-b?~(yt zX0N`2F-8wd{syc1Ahfu}WRjOLiCjYNna4IhhkG6zmb?W6EDGYnIRv8k!a3f2q16{= z@Qt=uSK-dKYe@3FS*4PG3N7|EGR`X)M<~Xx4SO3Ln{Hr)1;O&?lqJYTWcc|Q+d|-T zG$>#0j^F18x4+8nGI+l3aE!ms$6FWwCfTs{jSE;Z#N2>1EFN>b94ud;j3POMNuPGg zGC8lYlPslqkyJ^3W>JGP7czr~VSNk=%hL)Q^D*j%hEK`IJs_y#_ju41iP1oW=;D3( zaRdB}mjCki4-<2lFeEq@z%@=HuktP3Tue9QfD>X~Tu^Ym1PY7DM7Z70p#n)Ke{$ly zzs@EXuYS^{)j74=(7l7M7rYZ)vI|@-FF2zj!a5hPy=EZ`K?D73cWvyKr0zEbQJz{6 zM!Mz>F_(7yp&^~-Ss{~lY%t8HxzyC;g<Dpvv^h0~`5J|@ESxv*`u^^0>hKlol517l z7qRG+nbWgYwaaGa>vnp+zABCF*>W2KTC^6n%Kh<bbnp`_H;*i4A<DG84b!(C#y0s9 ziUZZt@}oi1Z5Hc$QdwUf)(15$K|)M*wkMJnQcV-}Epy73uvQ5a!0;KKL~waFx4K5k z?pccnCuyRomL0Th0s8sCsq!?ub@F3m5Hj|7d<e;$XtvS0PvG}=f6jr|OP;FSS4b&u zE)AkVM2L}&D?xt_`5w&ciZP>%<Gzbj0%-~diEZ;rZPkA;Lk@m~^@g>sruaUuEM;eX z#b=M5%$qYOtd$qSr-j+lh-Ada7mux{LA=&1CXl}~1;hQ<9C<3pp^VRvEw9b`j&B|- zD34nggdV@A^YJH@$E(Jm*Xp?C`F%!u)IC05YSt&&(jb=_(DC7~Egl9M1{nD9m_FH5 zPs7W=^U|xqK_QJ2@jJ&ke|Kur$W77teY8rypE1gd$i48dCH&NT`F#vnbuo;&3ezT^ zV>>^0Jz)<$Va7iK<J99TJW*?wv866V@sO%SQ0LG-YEl>6MVd38ORn+gc~NhGBv%Iy z^YzWchw<j#(^+5hMXE=pC(^vE9k#{8Eu?dcC)W&HlWa0od99fyM|O~Wi!oLCtPhcI zWIKt@j$+-RkzIOzK1Rk7Aqct<h2>JI7gZj;9^*UyfZa8tnCSQOb0TtW77-mg6TJH` zW{h(+4kfMrQ11jr+FWxf7}~w1N=>ZNJgO=H{r`Y|{TfpSJN(GlGr20ud}D-lnCbI) zr=F`pa}6zwcq`ULdRS`E9>|)=6elSn6x5*^e|J}FmHHN#uPT5J=4ES2l?|xXVVq6K z8>tG+_=E(i3^XbH_ldQaL$Cz-=a(c8S^a9B!T_ue)pR}dn;zIobg5#5fmHru<jkN_ z#S*KT2;d0jFx4{IMc{y|{+?3o%%utIv8Dup%A1nQ)Fq2M_hT3ih@`@v@i+Jjo7Wnk z?{qh1*QM-%fc*U02#nX=1ogy(YY*%Jk3{sThT<<hAK!g{d^ACW0SX#m#1YVuPGcKP zOFN+K@j4Xd;^Rx^qEOp<b<_2rYdrOVUg>>Y4xt+OiMRo+6V1<l3A%ue^rl;Bz*!No zI37R}F&kQRPmuNpVX;gA(L?h58ATsQv^<^j2Fr7kM!Boll2!75VrGXD=|g$K%YP*> zn@RPHjP7Si%uh?;(^FIUiQ^kb7^SBP_fAhWbbg(Vuk{Q$jr!uTBaFc8mZ?=nIRY=i zr%M{Z5$&+FCS3X)mw?mI_h`7IKfmqMb5PN1TAdTMCB7}S2#{lBx+kcP0wHKgESGXI zJLq{-=YO1*X|f}%5QquEi&c&gS)+9E+NN~trE4V@EH)JiUZ%L*B`zef+#=Xaem{S0 zYaPlbZJks8CTdQ4nq!^uC@tN;EY4M_S{IojR;o8gMtLd+?2HWaR>tj*MD@O%8%{R0 z!*yH;0R*i}$*970jOTS1Acs4o&wKWsL2T4pY-;V{Q_hn=qO1IhjT2y})D;%64R_pr z_uu?>YS1~Grw0NX-P=4Yx~iCOyX>k7I!`x^$4-lOpc}u@WoMk-7^htw=*9)?T$-Ko z+f@P~rwI__7nn}nBJWX1S!^ii#)@tfA_v%9?hcnt?X+=_k32S7gbhH&FST9>l7GC} zYoi5Hy$=4|#7PT;6W2_7t&G1l8s5Zf@M3;<L2oBpUdY<LnZD*3PCb=+{)w^?>pq*Z zeL1`BGaMkdf$}gM|6BT=-~Ykx*$7=YV9<ZPanqv#oywy_?vwHMo<O_`u8NX>?(C&m zU2|;1t|GJ=u2dDkm3|=ERY{j_Y%q;T27ujL8cfYesJ?<aHl5)739G~SK3IQH+fl#g z0mr3wQ+>N=FdVKg&D^ui%Thj8wRsb&5US385L2siGPHAabbw)y!Q_de<g*ySzAtv? zd*5>BQ5KL(UoYzPTc$Xc+!Ec(F)To0s=v8sMDM?`hhHo`j1D__#3g`g>qmQ>YUam_ zEE4lnPPN84nn4$Nmkl-2P%srSn##BqZ|w7~Ls`BcP@r?o)^9^=Ru;VFDaWY*rt!L+ zxC8!x>6%*B-?jRHo=VfJe*bWQQ%w&mv*g+6fK8od;|w2wcKY><Q%^eA`)!QJ#;(f< zYvK{2DmKUN-0)SFftt=2{+(^eD9s$T?eGw2GGM!mxx{*;2VOUAZ!9KFm~41^#eKgT zK5*H2vf)xQR)%HF4*^#?82Z||Zt1|E@NsN7yJv*>RlCRS^w=mzT{qtB(?#+6!&{e| ze}xrRw5hy|%EKUa8aevg4qx}>L>#-HE_#5tMCS565sM4k!c{nI51m0F@tfV;HKoYz zFPQAkRflKRY@76*66pJrFKqGn8bj;hAJ;;Vm%r@+mFsJbOT~ClOQ4<)4<M{CmS5(z zS=?1?MH|}|EX@pV^D~s+ruH!^BKb3v_fg8psKiJ0&PP?AM`_oi4B;d9lq!l5_y`u_ zp;1$!6!R`l4{-q}%^%VwK%86PwDUWv=&U>(2#?Al><apf<V`pTGphP9c`qOo`0887 zKfkZ#iW-84!vDL?(r5T>8!Camp-Dl3dOmKt-cR#|s9|xk4aEZ?YZjmWh)n9#l9w!o zt-)zytUG%&eHFwxMb$-KYaVZNvgR@KBkB1{*+tjB%zE`e?L53dGHMX4NT%oIQMqc$ z&hkzp_4<3UiFu_7meTxfB1eHvKe0hwb^ofG>ib8w%~QDBND!s_ZK{E)!9~zz-d0a( z%*`?9=Jd$T>6x3;D>o-GH>Y=QjwLsz&-}d&1WYNV?xCAgXn@JC;axM$TPt~HR%2li zJLuKYf`!|vhIuw0K3rq8K4G3RY|flSWZ7#K(*N+<lOVsd8d1S_^I<X>j{&Vzckv0$ z_7&`nerlJ2GF9^!)Y~}R-<auZ%;a^V8?VqU@irpD+BTrx(E=sXEwS2Nte>KHPNPy< zOpElT)upI%7tpI<;UvVP2G32WQd)Wa*-Z_-SFmE^By)n(X!aWW_>8^$MoYlhGtJoB zZtOANnCNo<-DR6J-}ac@HaX4qM8G!1Z+psTo9?we<Fw@z6x$jO*bX1J!QR$((>7?h zEpx1G-Xhz4+v9edBhB_?z&6!yo944U?X}Ht+H$3J!BZxYrb?P~?dbn&-lo793@ZjM zw^}u+No9i`!KK&a+k1QT2oNyjKM-rav^gWnJaKb<IJECAhZyOK>5P*VpVqD^W*b)$ z33U5vwq#1ag{k7Vt}0(eOt~df^V68)hD{%o%+9y4=lFg-!%ODmr?EE;{d%zwI^IMn zL4_)rt=|x%80IZ3A$H>Y*ceG?>KAY9mmJ<NJ&WJHE}pg4;ua`Qli4G0BiG{2v5mdY zyn_$zXA?|U-(`PKNGU93S0p5_S;MZ|$nmQPADxOW8FTM==C%vfco>SQqEBwcXMPSs zV)!N8@16Xt9PbjZmadxkRY;ef30qi*&*5u=Jfl0fg&3)pelr0x5W|`ZBes|eZ?Iw` z5MGGm=g41BC@wt5x-<p?kMenmup|bmZ#}u5E-jAXv!&0;=WKaV41yE(kk55id~UWX zpPPk+F<1s42RYD{t#wqPAX{2F#m^CTQ|?bla3eoQnh}Gi;-z~N6p1F^a=P~$ti4j3 z=0!bcc?ac2Pe9z4Zhac}RnvU3bzs7H%P`j&;R22|<N@p7VtfCc*%WWCsYk-K@NjI& z9LP>CJ9JA_A#GRU5v2Cey+UCu-g{GgYBRZ48r^j-P9q=Jd9Pee$;&#V--5!OAc~+% z&q5MWP7kz$7E>X>_9Si(!Ll(AKx9%=b|&}VoHB~n54RLgt0xAglG}Wpv;ZrH;`CY! zgurB?bCEt8|7uQ4)gj!b$wJ}oxr*i7ksXFxmUX}b>k(BulhZeEp__VBRcRhtD-Q@3 zwFS*j?Suwyp*E-N9B$!LFzVAW3POvJ1K#X#)afw5BSF+8nr7IU{MzP}(!oyF6Ulj_ zwEH)*RTg<l(CWBm+VH!Czv;jGqcpxq?sRK|<Bc2A&S2K@V4(RY$Cv+6-e7|A1`fR% zL%z}h53Eb1OHs$`;I+_#dIqyvf{qIT1W%b(1`O4X^Z<(3W<e@#KH*q;RUQ_Hu$6CP zICzs7`Iio^5&xwLG2EAkJ`tW(U?M$7U=kbT>WEFM5H_6=#&Nx;44!RUoYMu{uK!Ta zVm0vZ{P^4d_3u24<3?iKV6{`#($g7*<OwgwWd6flt#j0M)T@x~!)!uiuQWSG`7x8| z5+Q>%a0glu4tsJ9&id-Qmf5hqPc$@X!0PpOz1FEWc=S3LmWU!YdKEDc!&aP~y?XSe zBnW!rrK}jlY(fi}nB3x<p!H19VYoqMw&a>L#jcstj==n2>Wjys*wi=AuC>Ap;DQ-I z%IQ(7GV|G=c)tEk7~7+z8G?%(rFlM{tyc?{-_05O5pFQ?*jlUj@sjMdHS7JlH$A$y z@~_04O7ZFn@|U5Q3Azk--rwtY#qZ>gKQe<D4oXG>TKC7CS;-boV-ejMOP4%oK%Smr z_y;Xovx<<lmT`E|CWe9rMa*Z0G)6I53<<rKFd_!=dP<zm9`AUqI9}(%YZcp)k))(| zj#C;Y2|vU)&S0U;lw=U)Kjz44_UbhDc$mKzQ!>(kOc9d(g8872jV2pT3u8bmU<x=E z-|03Rj%z7o_d+(wNW`3Rh&O>gH{{`6>hiU$P!`kaMGF%d`hl9w(dzQ|So!@J68b~N zwnxzxL~d7V(_wNZ&-{b6{v*akGftZBvH^`RUV+EnXQj2F$H;TGb!@JCnUmoxQFMCo z>f!xA<DrMFiRu3tX-QKW<HBs~*F~z!%UEk^=VRo#!Rt|zd~|5;u66V3S2nN-LdL$# zG0qv27Gytcc|0cV@o1CON&CC*OQY{E+3SpqbVbILj4er0stkASN$(9rOKlIfBohl~ z`FgOK_l2HN2L*K~(2Tq*P3`E*@vNOtwS>`xQYmnIk<O*>x6{M<v`wt$Sv5@L1(OrI zKdWBTjVVP_;4I2xikVbp6SGvFuiHIY%9KQhdXa{`U}Ovm)~;vY(4T5{s;42@fwdL_ zenWZ2v75xgr^vV9$7@P~PbeZSGP<PBJ|@BwX&QGQ+W6&~0_thNtTEHPk&!-nX!BWr zxb}SSQ^?b(4;RqeFETQDj(bQ0&WumP;i)C9+e#W>m0JnR-s(NWhA*w<pdKdna7CKZ z*Sm5}7@KFdcE_D!WaQ2b`dW>oi?QdLiWsWOTdx69d)3pB_BCeO@%u#Y(Dc?5_Z>dW z`o=_9ub>;neLIo>oBIHpaez&_h;tbk?YEi+wKO1aNifMtJ)DuzgN9>03(y7vb8D?} zXlgDnWR7AZW}h>wKv}No+25<YK+QMwm*$l9zA?fe(nDKd;zHulIXvjGnhr4lfM7yb zi@rTDUeggoKu<G}rFkMF5A;^l^lH`4&vk%H3l5|p2M?$`aE@@zc=bb!VC?Y$gWweG zYY0CeW9I11thefOY)8hS#I^-y#OFp^8Y2R>h{HcQJzvc@xu<#0ZdE1`H#19zu1lsf z^H3FOY?%sWQ^$<8=<es45@#qD;xa<2u5M(Vs3H{aG3t?EtG0~cEMqj5$*g6vI_W;k zm<S8jIwjIGB}yDO%reDf8F!y$+)(GZ`vcvmz_{U6uURJ3CF#P|=%m*yV`8O~@m*0C zUF+qw{;JQ9Y+%hy#y1deQRuWS;Do8-cI$=K6RJU2X6m4ALwBZ<hPGu$X~Y7Z<A$|3 z2PuNt+n^4jDnh0@JE(>djtO3F-@s=4JMr*>)2&WG<Nx>4!&MgMeqLLWhv21A_rrn| zT3S0^TKF}(YOmBS26ivNu}Y0MkfZVPqwcqU6Yo-weH%^qTw}(D9>ALb{#zr<H?bLC zZyVyQsgS-3p2`OK^rKk6IM7~C1KS>%X5~Pnj(otLJR0cn=9%?ua?t*$)*C);N5;v( zJyEqZ&cI7i@{f3?lSX8GWr<Q(Ar;q|yLU#i)kzPt&;v}v4Ln;i#}3!`spxS`*<(&` zJ9L`T0n1GRt&fSVdi7GaCrX{sf})gs9r`pSL|MOyu70&;L)l{q>vxQpH|{Y=C}F-) zvB~ur-$0u3N2zP7+O9ZP*TCu2GwNi`*_3;D!{L`2X3SV{h?8%dMU3_}S`F@cwMG*4 z_Y13o<6h;%NZwuI0t^FZEBw>6fM6{qHo29jQR4}h9roYJgf~XKGf{^c(TaR!11uMm zG$VW9ao6Zr>)~irXjtH4IZ(bkNuKD?qP;X#w^KCSf96Bh-O}LL8ttQ1r!SyuVT0px zlrteFK%*`knprhCf{~xU+VvE#uiN?h+CDAoy$R7ZTgB#f1V;Gqu~zX$yGC<6&_qOs z|0CPn+gQbnl+kiZC$jI0kF#R)85l>!$JIrs?i8(VsBzY87UQE^)<0lLYi<YO!Tlrk zB~K+-41{%~n?_zVyNk_bxsnH7G!*HnKUYx)gFFDfEBaJ=zLh`n&rVLtNf+Bue*ihP zdeaWgY;+}|MUM01jlTip=fFpP;VfW3wPw<lhSR67pH~fgFp?O(grnwkOZwzVlddCH zT#a&Fhr$J)xmcAv02F2wxsK(yFif+1y>r7lS6o!?<Mb2j!x2MV&&l(GknkU;ce9S4 zua~=7E2%q-|I$D4U!nQs3yC5kzaK0tXSe^0ZIsv}mF$@c_NvJCUQfVfALFuT-SV9G zp70+Gc=94vio)?|F|Tju^Zo2fI5SD~R>s3a7JAH|_?vGJSJ;t-8B;?{j7a2W$glss zH!E~682=sc2hg{UU`-$8PUW8I^x(~q^Yix38p{xtVy;&s5Q3F=m`j!<SXK{te(kTM za&l<A&1{-?R&gXB51Dy2r21>Up(7;aRKt`x68^`$$p+V$So>EIX_NKEk+sA~xEgNQ z{Iyr|CL`)t8N-H~nGums4sEd}4MpByOv&TZ*|E`%zXg*z<%^jpQ&nEg60(#s2-Gu0 zAxp{YRV|ClYgpMB(?n4RbO)0|)TxyTrBV=j^vV`?W-n%8WLob2wj@<vE}fT)E~==N zr<$lxxnwe2G8yh|W~}N(W|nm}JI)#!Cj_%X3Nu@Bw4m^?B8;{-Mw_+H#t1^m`(!GW zpFzpXy4GUVk+tm7?M^-f8PFUaqE!P_{>BJjW3;aa<?l)Pb7>!X3xnRwq-LUOI4s6E zoAh6K!*sUH^tP&4wNkgz)J5<hXOEjX<(1H4%uT!otuFLRvkS2?!qpi4N;TWr@1oC) zuA0<k{X3x~1f#LCV%^IAXq7U-95zFt<W(wPV+4w(dCe3S7itH%TW?-{h%>i%8e>=2 z>@s*8%?EfvG@mBWKdl%sPUlhW;bq;G)pbAJsjG+Zp_(1gB^Lp|h)EFFXHs%q^!BZ6 zZ-N%JTvFt0+8G(X%dqJ{V=pG5=A7lc!TS{LeTwl#cH3>KtM^5QuikSk{TJY&?HV)N z*>-`s#oG7ILV%{8V9=0<*It`#&&xIB*<&HWRk5}$>_~L?(HBRBP;5xT<othJ8L2`1 zNu0|b`+t5ZbLQph9V<DEo>R+8{Y}I|Gq6;NCx(TFH&oHps**Y9^ylf8@TAC6<rPQj z5OLe>^HI(ydNwn!cpH30L{ltd)7P(Oi`0}f*+jiUS)Ve6(B}|Sg%F}mQ;$1MhhQ=0 zbp;ogo9sDyRqz(;c&B4{TFLTa^8D~9$7AqW{e|PARE}fuAIH^>4{l4-O&k)9Lo(!a zdedm0PPDi)Eu|LkVY^zDFxl}-2S^+dJ#kOrN3d1=yZ6WJB{;RVZ)5poQjr;-wzJax z<iCftD*r!WrTg$*7-e=73Zd*BL?naP{i$`WSsudiC25E`&W#qm(_jEcoSj{jX2)MP z%?_KbTxw3sGZucpq^>Ogl;xj-ecR4^>|tVV3`9X^?w_`b&>13iAkbPXgDaS<n)Kx0 z=l>CHr&R@`RheT4JNFfvjvI3KRheEx4G^5L$5Ve>tU`G*rU@>R;!wJ5@}l_~s$ijN z-lD-J5C7hsUQzkWwz7tT^QzP51{>BFtRyAO7M>q$_&2XF_^`pSzHZUH`2~MIGJpT! z{Z-o6tJU)sy4w~SYc?mWHx%b<PY|f->;9cklJGeac|$X8+ch|UBv+=Y)-|cA1C0Ch zg2OKw&3~UUxq*twq^^`B0q7voF+zptT!-#Bl5X^9JM$TPzEfY?zJo2_$&UY8ZVPVT z#g^~JN2^kNgy^{AGt!;z4_)rnE}P1!xCqeR4fXyEgX`GFJ;eI7)GWkuupb+w!MDmk zCs~yIR$iSuN|ieb4W%hzv6<JQ0|-Jgj?}YW_gmhH^IzW*218!L`aw0Nmh87->dT#o zeH;uo$W{#ax|uiF3J?(wEPo9Td*Dn*y2Odn^X}BG4F{c>SKQ(w)Zx+v&3kjF(c%QX zIKd!JK<s;8)5`YpFWBvWV-N-ViWw_;<x2#_)@rP;m<KnownjG3{V7ZUr-E~E8vkQ` zjv8FgmU}^3z2yVsmoTl@ee=j(*0zr|hx<pV#C@?KFoADTwEkl5qnC3ZJ(v6FdH=pP z-_w+Ju35nK$FUo0cCc5!Vx9Ol_D=5Dj@+^Bxlb+)Jh{YmBA7e-At87PgiZm9Jj-le zh0Hg;MCjXnNAg>N#zc@<zK6G)%F~U4lOK{rN)S0-p%iUYSlXF7WZT9>=rGO$s@JRf zm+tLdTF{q@%?#y%C9J33E>A2Tgz`+y|3*k>Y1!ZcIA<qNH<Gv1YCITvt2NgQorb+a zT#Q13-urFP)91lut%&gV%zaS!Q78SVlZg){L<j4@lZi17v=6%r8<R(5cP58#PPq>W zV;)*3Z)UUlQl9ePcP2l$Ipq;P>}J|6qrV$s0Ci$F&P!`=Lp+gQ;;W3-`=UwT01;-P zSuiVQp;9ZYB}WeVR?8_aih{wYhAv8iX{q08(b`v?6^?Ol<66^T3J-90O04v(_IPPM z8q_|^!X@M>zd;^(5|7YXQ>g7ttTW-Q&9Suj)LRf8NKA@H)RH>}k^!2AVSrCN2auj& z6(d(T`FR-mx#&^YA++@>#3d}zH>%Liv+AVFP>Iyy>L0q#{-_mY&<3scAU*6s`G6to zY|z|(3`@(Ver+x~U^!@nUWMAjXxVDeZrMYe9K>TT@oHallj+4?c>RalxsyRp`7KPy z*~o+_^(?K8H(iM1H{kN>Mt-87)(0ZlKo0A->7}=W+`nOk)8PK5hm`yG2Um;oW2&qJ zxXh4sXDaJ%M0%svx{`$^=MnYf_R-4qJ><IJWV2AvUkp2o>%*zdAJIjh1#80z6m$UN z6(0775Xo><T|(yJ1KWi;@bT(f^{%gPI(A1x!jqP>MO(1)|7@#kaBMu!^Wbwv4}y9I zUVs`YKd-Ga3f-fdM9gj^t4b!bqy9c-PuMO9dQUtgbvZ|!wIS%Jkpr~W(d}1hPY|{E zo#i)?<?49y7lJgtZdwIzv<lwn2;K-Ifj5#dx@mRLDRSi2sxz)W521^><HQ-jVl0*Q zcP#7<S(xGUYEfWyTw%~F@(s-mjypdR{CWT(x?$Go6t^_!6Lp7A!qC`?>p)LI&o#XU z$V8w_i{qYCxR|nCnySBfa-@FYTPd(SMMVqa)bN_E_Bi1)qV@d|msh!+50l1di=GcE zAB6|lrst5-vK}Nq)wg$mus~hDpDjCznuzO<W<4Dwp;hCtu@I)4rUuJSmhNLs8Q+(l zV@*GP=T$L$eCX85=PB;7P(I-Swu(Un%|TY47hQ$gF2b)!0}RQ$HvGm8KFGodz*t|7 zt|BorKP4daJssa)g5n3hY5uwYCx3=t<GZU*>Z#civaNT5;<Kr|VZsEdw;9(8n_1@+ zs$V4E&l1@$ExI4PF5tTZ5zuEj2+`GY5TjWR5<wkfKr*RgCkdtV@=Mc9V-qI#UyIT{ z%WgWe-tN%`S6%*R{;qo7xR-O%sx@_N2&HDcURt`aRMfnkkRQrQU9lm-qbY%d#HV?8 z=LW<KGsiaR_(*^Ev3}BE5jtc3j)PtCb!j%0#YW}Y=sX+B@|D-Ir6)?=t)<Sew-eTf z#?RMAz<fS8&0Uf{5;UAOXE78wNB-__%fDvjY0*4WkK7JF!}t)@bwx$B8ZWfM>kAiP z!4>0$mXj^kCu2lJkrSC?CY4amXMjoBD40LH!3Rc%7GSqAwW=4kBOq^6wrFJJD?#!X zc4J%wdvt6-y>tckUHgQ3_jN4KTHN&OV%VVN?t~$eic9Td7r{iM+{deD0-m3_XY46O zfgP`5QU9_&`Q}8QVTtGKWy3z`o^LTARBD;^w}h4&PL0k9{9NHmLP<?$_mzEQx{&R0 zCe>n>Zw8B^DKcYso^!m4x<WJlg%V2i>s3A6b?$_kNqb6NhQ$aU?Y~~{Gc3yQ!RC!$ z#G6O9ooeBArPc09HKiW?qJ;IQ5-iz^)3TS$&t8P^7*F<6G7W&aBk2U<LPE}@L#*|B zuwVQyT@O~n<%UNu#n-b{*atj{0Wgf%yiB^7uuGM1q4QWSZ~Rj3QcxLcKR>_d60tY& z2CO(CZKEAhpvygJeW_EoSkx`bkL{Zu-&@oz*;MB_xGX=O03t*B5rBaHims1y<<^9f zI=;(8v$r2IVqt~t!oQi+3wh9rVqU3ZGrvWiaH%J3u|I54eo9V$&qu_tC9bfgrg(LJ zTxx#&L&)XH@Elr(&f3QlP!b`AGf=x=y65Lg#}fEn)MVqNNrT<bEXTb4MriYsx;gt8 z&z{qH4z{CGpmp2S<x13fe&6>=;v#9%7CNJdWBh{T+x(EAuvZBuO5vlS-)B;OcQB`g zH7`P#;gLGl!>BCoPeZIdF~#W;Gm{5yu&gl023pRUo-sOG&YEtj&AZoangnyuQ(Ba) z!O`*3s<ZJA^_!OLvlyRf?@Q*9Kh*iHFZX{b+((1Oq1GPZQo;`gj2{{tIq7dSLX&&5 z;cGN3c+1)xMCi8Q4U#>!{hRfKQM@y8OGJOebAhk`uj~IZhFy9cLNxk@C)6~5<Kzo~ zLHSSWo%~y8W8p_DA{{4wyc6dMQ(f9f4vt40URN))B#3UIUgc#AeXP^ZF7n$D<)Zf5 zAmau3fmXHFqi(=FrrW<;*up^c$ii>f{$FaH$AfLK-Hk28GC#zcZ~GDs9X|9)XzAt; z8X9{yw79`ir7wbT0$DExo4z6S5@6v~NkQF<`ZV<Wi;s5Y(9ZZ+yNv_INGW&bu+qn@ z*w6UHoM;aRzn&MzpX(&IF3=hur^+AM4_<MPsk^W);8#&M-VAT0#Y(fM=^h1sjG>k@ z8-&5I=+$y^g_3qg=ppt1Oa*3MRC|#=yNn!XdK}}W)j}MKIjwn-aaxT0cv3|u&nOLq zNnObfMXrO>l2S5DbZ@N@h4P@sl3MbD=-yT%N&rodGl$t-zKj;HojHwqH9pN!ozG5+ zWg4vav0jT2hozyNHFeV=-Gtem&m2M39Us#Ay?MrlLKa!eC=z5UX8kd&=8Bz9ucr^+ zi3sEw<$ngvJiHz%*^?lzx>*k;8AS?3?_9xjQS>hJH1_3NtlJX^)>K?0QfTUBMz|OD z3^IguQ_xe}Fi}5AY#<d=KDADbuScRi@yowU>?jB!lKLrzW55LB)()T^Y|-F@fX{=% zCPT5W`z&a0@R;q!ZG3X$5!OdD<_8NtQ9(VC%Y)_&yf01$o}amn$+Z{@b4YLE+&trs zSJef7PXqwF&ee1pnM<tfLPs;RS(XwkeN)i{H^bTWd%;@O_E#7YL(~BriDI77sl8M> ze6XST&P~NJ^==T=mNw!&e^V9+@LyJYzK#}HWT<y74K+vu7Nhwc6vQOJTNgC1L=06Y zG*%-<*<113Wwu@2N}~GmadgO;FQ*#o*sD3r%(;v`heyNCmBt&d4s`4fck!3<<0b>7 z8cb?l?a5JzsF+Z35xYQBlF?vZ1sH#T`gsVHJ?7}a`T0wRD~>O;$}Qxv03IuL=BNTW zXdq4r;mQ*-RP+E!1Bw+Fa1p3+Su;by19+S}^yF0~gnp~x-CLV*hG#${NWy#`$|Ck? zQ=SU#Pq+)9dWC7bl{iBw0E+h&ZigOU7JB^MgsjealqW80gq!#s6V}tA39U<%8?A3A zaB{G7(PySG&WhhKY3f6P9F;SNLdyPS4W=hAKpVOMZGHy;!wN!}&_<ft8|8waxdiy2 z^k7nGCLfh%=hN(HX;0u>NEIJ7FDY1&xbmn_cBfzgQKIec;_OkI;G#h@XOMgLvbvqK z#$#jnWH_EcvFlNN9L*Pu!pqn_GcPz3YT4~R!cZ3{oT^z`zWpftpw_~K&<HQLs+18- z7=eUF38#_$8d@{Mn4gwk51OZnqX@<->vQ!n?p$mdb_<qA$cpk-78eS?Wed=<;#mc8 zfg6W3JVI&0cf1Ny067Zi3lU&xNH0_n&C;B#%Fj|HW8li|D?$u~#HOh_ek$-HQfI0| z^Ji-}ohe-lNR__FA_YjK)rK0BN89mgxL6qn*+w~Q;nG$;&>{5YK~JMu0)oP8=4a72 z=f83f3$olL_Z$q!XOLJz6%Jwp7UQl!2;;3<IB`R#^fDLP8|nFAo}EFld1ZqGUmZ_< zC$)#y81BuX_|&!^m-7$$auj_21^yiZ7@Xz&_YG?%5h_SvPtNk)i|r|w>+4yu;&$h9 z<v(=)JEt&%%!$E3;}wK{3};t(2DxJODzo<Ws<^s?3Fk#i>IR=LZ0-i+;6|S=U2wQ& z5cGYZnOK3$eL}ybZwT|ndYq9_nscgCljULRQ`x)=F|dzoT{P0pTnF1t2i2)KfGv~3 zMwh~_h*ZHXJL~Q-e2v^^AAII<jo-2Eiwz#cA$=GVaDUo%8~pJ0oj^Cj4-W?$+BPnS z`6V^3B#8*m{kRGMwVt!OFW*QnOLZco2KI|B+v@O8-RDjNg@=ZfseJ~O<Bgl9Y(;P+ zxP<rAfQioaxQpvN)+ZBgylUXLkXTC%zYO7#;5JL=+cdstYGycdXW(M>NPg$%3yw6f z4Hp~0SUradU;i-P?*24$l(VifpnvP+=>7jxJM{(m19Fs5gRDr<XLwQfkxfaB+q`I~ z7)`B<S7l5UA&JG^0CMVFHcgdp=bRY8u0(W~fh|3nu*-**^}E3+5dv!ck{w7$Azag- zgYHkIi3yNN4tCVC!dF3WUt|Bd9Avc==?iAQjXM7A&KLh2snR+=Vb`~0q;HF^AVyBA zV?#1vYMu3r*09PkT%8JbBm1@a@#6qKcwP~SKIrmxh_rF8@pHu&;t3;^B6KYm#LC{= zzYn4PWlkRXCqdzV>_pd$Ab$!(1YRYqvhv;#NhqjLxvK_SXQR$Q=<F6McxAl@cqNZe zJ_`ebUhgte*9zQGZn}WjKG>6KowfY}SyE$!HSID!fW1XIY2zVg5K!B0l<vEnX6F?C zk`x5)5wyby6*r}Wow$didv>w$`SWTg9c3B|j*dbl9Jt|{I>bNx)-Ub>0CCh`7^ghI zQ041BM`4`un7bG!-w}F8;hrFWE5w(Q%@YVm4G;Ywrv}YBRBTaLF7eL;_c4D4`}}zp z({H_rd%Yij8M?XQAQ2vJyc!OgUVi;Di|8J^!t0W~73VIq)&apRM6A)$0*L<P45^jA zys`JCjnR9`sj~0HkAi5=g9Puy4a?Vmm+4+0{~U2@&Zaz~aCzO&`P59tN2nN;QSBN7 zw1FcN0qgNSJyzrXrE2M$8~7?+x=Q};zJB04pSu7B{ttd9#+W>tAA_^dQ*qAv1qd7& z=0o(-;*;f`!Ho4#Bd``sp|9qo*IBdP%P?g}oh|#_C<J~K-7+$^>_koA<N`Y}$wE3q zr7Jb69;iH{^!elZ+?cNs8Po<6hF%ttU`s?5h5}>z0WnM9n;SJ*9F;#M416P17vdWn z_B8$w+seqlp}KJ=eFHX*x0a%a0RSPrizpA(j^U`dBhL(Gu@uqDC6pJ6PkrhT(@G1M zg4lx1vr)f`U?i=UUxzpu;vc83rPe^f|3tgBIkiUErrkOf$b{lKH0f^t4@I%Td0jZG z+kY<$?=e}o6sa9>Ku(|9S!pU*j{0KiHOE-b-uKo;JwduUS6tb@b^;8m93w>rr)XT9 zs^17*zi}KyRZfm!0b|i*;xoKist~M&%>>pQlktTDYpXVxQSZybzMcEtjcoq!ry$PE zBnDHHt2F^m75Jvz^r*U?6O8OGjRGD{ZUve+ITioqjp1C`_m|b(XWzqrab8o)v~63! z&-J;BPkt}uvF9|7NB+QZAuE%$K|bF60)WfOC-8K;I_nxbmV<f<s}SBGPr$VhoHl)8 z7{xO}Q?zKOtWDAGVp|IQsEET=rC5f2ii85tPq1Fzq&eTaQx4%DwC7OfDhN;5VR;GU zwR(DdqxBbzS-y_(s40{w1kSjgHU4Y9wlRlkIEcPYMktu$g?S8k>FVv|DElgI_;QdG zMoF{ueugvkWPhEXEPd3m@e>7e_y@&NI<ew+p;(7f0}A70n2bk+>mA5bA!5Q^S09uE z9T_9gNifjIh!P2;%DM5^`8`qii-C|b14|iYw8|Zw;}RNjOI~sJurJA1Ix=Qwq5G`D zV?EF@CiK5J0(6AbWr4V>qeOQ*s&ta{LI_(P7EX`G3V4Iek6fjgK<*@eZjLW^oHsX{ z{QSvPKo@XB;J@Eu?0*ILE8Py_<Jk@^p-pjbxN;FxAPq&R9IlFX)#jH_*eis>+2onC z9fSTN@3$iQ5~yFK-IR@J%;nmWlVT-Y0-bDL`c98$ykO7LDSqVDCz$cuQHIP9u`lmW zy$Ti{7o6elc2d$O`)+mV-yL_UONTl<IB~G+={=FrFw3>l*1@ds4+I%T?~GR?yjt2q zl&)twpiM3d5j(3NZ5{-I(Bz%*{`D$30gduLZ?@I3PL}FFs~@o<6{n|{Hxjw;Sp2DM zMJ+ha%0b9%sH63VQWh344gw2t^i`Bh@-~Qkk#m9tn@3Sd(fj_2y0A4T+0qTjs&cyD zLShv$O?aYIFiiOw+6R3chNsrUh$BT6@lV^XJ8%Cd(Ce|Cz3vT}6W%+|T|=AHzuuZ! z<F0fjjIP-l&>Kik&3_^$kSgd)YAE4YUT-hD7)*GUXJ4sf>p6a|C>}_2e>!L=f)Hvd z<mUL6+f$c2wQssm39Rg(%e{DA<lfG?4x`qM`N|5HI<!96$cVjwWIQ6ybthU8evhU6 zpUKsjyy>T)X><qd&<ZX(rBf?hdQjGa<j|U&mD^XiwAfBj&vLhep33z852(ErXZvTw z`6@p>78ym%(UD<SkFmeI`iqQ;y9!A1`t9EVh@YZ6@T%2LZNbB_A1lG$`5VqJ3uOJS za5d|B<co%EXmmzX&JQek(==y-CH=lMrU0J*=hrG^Gv6f>R!`(Aomd$Qy=y%Os=jVf zJkB<e4soWTqNeB@;~7sMBS~L-)_7xnxKd18-O63oSYOAI{`*x}-nrKb6TqIApJHWG zREQ-22ULN^S$t1kf25Vgc!y~3OjBm!b$dm?l0(WPWAr0+?8BD16M1JvpnnxzbU&<F z4T%dN=|=>8eT;zB;Wfyl9xBEo)0=f%7=p5kjS**FV9Y#JDBrV2X-B;L&hHRJe2s&k z!tb4HjlUr7g$h5EZ;?NZ#|tSc{2-z@T8HS!>tiyvZ-qz~S%JU4{wS<BhSIz+9aITI zoJ6zsQK(G9C#3T7Jq*xJtQ@-8^^m6ryKNh5`g&MKIz*LmT2nGUNm*J`&HWiT6Vh0% zNsmvkftyy{SIcB>TX0!}>X2N(ICP52`f~&UiMQhRe#=bH%$WYE%W9cJ>Q<WnH#7pi zSLy0RJV5lZdz6jzx?kUq^$n-`Z>708SX@K-J*;~R+VX)GTQh@1m>~KHw6B-s4?B3h zd9HU9<1r4EZ!jpa1VL*vB3(yN{ozu{f}ymYIYhUrFLUi0sf^A-ou$elJ2C$KRDXC| z_6IRHhP~BMi!5K1rbLsGQJl3G!+na8e#1ybBgsfjd1P;p-FBmcPre$?_=y^co*@2b zjq<7vqFT!z#1keKS{eV2@Q)7j9KP5&ijhD1gRr`zrm3l9F<$q4vKD!~Dr+Nrov&dn zS(#)lO`iq*jM}1RETg!@D-D)P%5fzIuSMh<>v>cl^CF%U98I67Xs^;WhFcwURop0b zA^7=x*LzB(LE`?XOxxyZa6ENRc{|}E-VU$Edq;72Nx{ezEerZ8ZXNbDI0pV&-z}sk zDpJoMMC##WUkA4!=&!hP+TY*^|3!?tO9Jahqyn(CkW?TSb!6-YU(1?>{Iz!*ybTWX znN)K%=Td8aU7$9d=>4w+tl^gFnchxDjrxn-3jSFB>jJode_u_u=pdI0^O5ia)AV}K z<t=ecL3#$hpJdESq>X=A+{MX#OwDm2sLgAo3EZS>RMy0xv!boQ2_MnPBR{f@>p~Ts zQHad=uLasOIn3#cWM8@<LY@+4tC}7{u7!dNILZ8@!kvz+2jGeU_L{XORKHD@;<TnZ zg9320n&9Lk3~E|3oRl9loHPw=sU>^Pk55$gT=<O^D{j2d#qtGC7eI0u!<f2-b)siR zPwQ(P8F5*=f`vb^mL6~LJz&s0ZAr8|`9@~qf`0)92&%`aGcW6~c1LI*6nqBR)}~c@ zDELO4Cp{l`XFXP9j>q;NWU}tTSsdVLoOD^^tsI&$-;$H$%^9E&Eh$l-#AVC}OEC^j zom$NaO-z#|cw`0R#e(6Uj>2`)D(hKe6({Y(TOqyhXF9;BO+SS}*kmNM;H=Z7)<`-4 z0{f4B+RCGF6enMyiz2$toB1Q_tPbq#--5Ma*6$haQU|X?eHg`X;_71*MfCh4vJ+(9 z=i=|^x5JQ~z-lfISSp3=DRbjP`}lI`ELx9ISwD1iBETM|=mwZEmeQ;^XF8AfBl3(6 z^EkNGuo2~~^>_&8gtU-T8U<_)z5f;MT{PhfX?lnG3;!sl=?t(Q;caRk&d^8l?;S<0 zO5Zxj$Qr_oqDQ*+y=Pj<eV{3VKu^e+!0>U4x%{|9uq`;^cgA<KUcGkUXs04_6fqmM zOd@E&>x$U#kD(kd-4QGGFb5I2P)++rQSuyEF6!w{D)6BEIH1Ho6d1)IYmoea<0nn+ z5aesni;?1{5C*{mV<V=Y&MfAT=kb%`^K({QSP}h@tKwqmdkv;39Bd<-qY;Q!&nm=> zY3HmqWfh%+3X}1&SR3dn%YVUllTLIHrdX@?RH7=Yyi$t{{jT|Qq2=u6aPzP8A5thW z4uqf$X6dQU+pR0KTNSe(Rx3Z+A*xjfig^qYGHMKBdPYc8v<==%xFKf5^H3>Jgaf?Q zSsAt>x~Ho$c3pI)SaD@@bmHBMmhV#*E$*F$5&|W1c06H8o$IoWBeeVhGJ!QdXi+O# zZU|xzgXsPV)HkJvI<}u?6Gb<?I?F)%$8pHYt4A-ux@whmW|s=bKUi(V=Eym^<Hr0R z;e}^d{$6A{T@EYwZS8p@pDj%Oqhv{$aoO#DNvdk&F5>}Xzl5pBkNPWNo=iM(w4Zrm zekw}F6N3F>2xa+L=NOIfI92rTVE%*{`xuDFcl|2mQjmSfg_k;SzfjGI<R;X_ZE8e8 z4|y2V^d-?%ot1_af89)>@7<dz4ezk-2T~~lNlKkq$y<$}i(PDwe@VEPv}2yF=oA0{ z@p}DbydfP{?_b9wboS5j9ubnscp?1+<MB^m7ZJ6`75uHi6w8T~$(!FB+qp~fJ$LWL z({jI#`J)g75vvIGM1ZpP(Kq_pT}L55?LNvOkT<k*{3F)B9sES!DEPPki^OvD-Rra_ zb#!8eH7WkwQP9)P9ba}m|Fkvnf2Wenp*9sne#d#K)$p1X(Mi=tSLLVV)FeR<ySO=e z==Njm&S)#FxqqG=AxxPT=w5OAkjDBX$ZZ%;FNTy-{xd5riEM&o8Tw6dXieF$C@FYo z?bWfE1FeHhxqmSs62iX!5EA28t1_k^3O{{lc`FAS=pmJLUk7e0{hc+v3~8#KPK|Yr zij+Kb)0r+JAjD&+t`o~y8#|8t!ulcSHj{p7r(2wsLvxc;Pqw&HQ{96WES!y?*Lo(k zhi}50!bdcEjwytVC3tqzi2D3ZUn`O@)aKBY9Fp%uj%P?ObXI=4BDyz>0qcNuFtq+j zLfSisWQKPZUWkN0xAQfnh(BowC++NLlvx*6cAIqcEB&XlUf-M~S_UE0zyAeOeYX%2 z(QoljTe%LjC$PVjbs;FWEUT&h^({juZSZ!W^;+n&^>7Cwx3OP<d@J^A8!&7DF;u%N z8%%TbCyd^Ti~FNvj~ru<Mfdy5mIaix)~c~zBekYEFi|OcBCA7tvVU=JvLRcrA%qO7 zS~(|n<(bRcg5Q?=b13T;vLg#;cT;{&jdNi?*3(57f@g0<Cb}0uF6e}v^}!%K7lcPz zU!@&2H)|PbOh?h_F4)K&?Jy&fRY*gDHHw*!h*a(Ei#g1{>zZu4bm{^>5h`J7iGm9r z@DdUZ2O5vKBPoYGk2}u{Vtsv#yRH#O3}nbPs6_JTdHh1S+RlForcnfEEZIe(vm|4h z0uOPLp@!p)_N%`lS@s|YnZ&2ogalE?8=5sgv4|#f#zEXT3@LHS8$0H!T6uykJddow z(hf(unn)VL+ZxjbmY*uDLIp8)k#Wa-#T>5=(VU+003B2f#T>63>E+rguAY@Mng4n| z_z$gT+xZRRD2AZWuA{qEn=k4KatRMI2aX<u)11JxU2|OJ-x4GaAMV7_Ry8b$ojRCv z-`bQlh$YXVJX0vsSQaD%lyiUdqh#yf8^gc5n}dedX}N*h$X^+YZ3IPxCi9F=G=e2b z7vTRx>2-x;?CfY_sJnI&lH#SiX^Zf4d)d*==e5cbhr&P@sEl=P3BoV4@+f?10kwwv zCB(mauZX_)|DF}0S;0S?!sKdBMuv9$E{;qrecZvp_|}Pxr`lm5fV`fXEiBZ)f|o5U z&}^N`$K}TXzV~!0nc%j^bqeo*Ejpx_P*l^|Ao4-~6XN}q_x>+`@G7%+9nkOFfjjVX z?SF9`oLY0C8<c(!0olvyZTIv^HS?aY`&z$4kApvX<~u@5JJgX9a7$UfoqZJb8I%Qt zbP+__0V&VE5&isS<ikgq-bQXE4w3MI;)^&_$NIy6OKez>-?Nu_iPe7_6K9Vy&4E3I za695*gM@8Or<$$>kG&XG&k%V)?32pBD>nC)$8?}#H4u=Odv?Gkp$sga=s?Bl+Aw)A z)ATD!2!)()jd7>yx`f4pXk<$U?-CY=QbngotszDlKybNB|9eFKqTM{+1L^8J!dDBA z!G{zeOiQaF^kjq9Q}Nr*ztTLp_HIbZjqQLEBYo7asBn#cfthu87tAEzZADTmxqTPZ zq?ak;1PK$YO2&eDiEuasnynZ90vTZdg{<c;45@1;S_q&?poGfwLpqv}@zn`}?RSA- z_y2&PFI5D(L<~`}e*pp3ZXW9d2z~_aG(xs85df5hyD9?fqSK+n5=iKEo{Ad;5_j=l z>j!s3LfGC;R!zvDuvOXjE@b<dARCb2a3gElWbpT7PoHKT;f8v3t?DW<MYOd?1b})U z#aWAPbOjUaohup7E0zcZ69`-5x-1bSm_Q>XYf+E63n0~@9e7{a0jc^gJ0QEs4iF_; zstfIasiqU{6eU`Cf<mS8&XKHAJCL@PId>GrV_~2^DtyurI*ryUx~W;w{<`4)u3+xq znt+0mj^XwZ+?me3K?8qydPOvo1d6r=x*h)@AD_g*7>nf-<z^Hd>{qO}iNC;_*M<Vg zL@};7huj`%Xl%WPXvHDFgz%5{j1M5{3_uuA2rpN>2jUnKZiTrDcth8xb1S05c+ORE zWnFYKYTDs})Rf&^)zMVTiaFgek(ZR;3YP|4gJ|k5P()KQY8UkW(N664QKmEo&6X4o zNop(_PcoO8kb|V8ou^R;o5%rYDCfR4Vq+xSdO2DbTFw=--hHK}45LDElfX&Vz0@bb zsCGOA3%&4tJJE{bQ&SY`3aOfKjXN}HeR#JBAuqZciR6;@rJtF37^1|PM!6*Jz|kk> zcDqZ{k$$ER91VQ@Q`?=0TSo(ZjOO>+E~Dp31fO2`0*DxC2@PKOTDv$iLYR5O(;Idz z5}MN6C|x@DCpN8!>xxTJxPheB;$aMiR$suww)fPg#XzDpvj|oEC!vb+ymq+smJzv? zvCeB(c-&<TF{cNF|7h+aCm+g-+wBlg|ErV8Z?*sLI|#?fJM=NSKK`k669PKP8+@pN z*}o!y{^0xzXh#Z7n@{!R=)@cnw?WKv3?*OFUOmemI0`T~_*i1*e%5GuktEiC+1}(2 z{%bzX4J5KMBMW4;uY^`B{AIP$g}<!U`f_{G(clC<HYP&zdCdg96sGw+otWr^Zknw% zu+Z_Et?9%-CxmM@)xbVy&}>Sdf>>sp6GDF4NpZdEZb*d(NhcWfr}1ib*SWJUC){EK zNLHuas_bY;(_eoSj(}bi%wz<B#JLB1Tu-w2=&X-|4WF>4@i&x-mc2n5ksq~w+0Iof zQmR*K4RJl;O)9GrXHYmoqj5N)UHjIgloRx%2zplOeeQY3qVGF%j6M5T8ZPB1NY{m{ z(w4h%Rk*){AZhN$KXslF>Dm9Yht@sV!%3Y;NH*}EuuymaKfC+|1giwxA4iDK;<R6M zVl5$m(J}2AvVWk34IVaRel@*7_OGJ7sgI)N5N&yyasUuS0}R12s6G<%>R6}!pR*u) zu!gZm$d-rj*Z=WPr!PgEKN`YgX-s={qde%wu0|i~ZjoA4pNEb>GzjVwp(7B{sUk|> zg;dclz{<0fjG6!0RO<w?sViL&0R(L7)(f3GT6NSTQV5T#XR5KM0!?vk!C`Y7|FU^_ zN7Q->lXlQx+@K}paQ<&Uhs+D<kvMVRyQXBVXWu)fjbV+)*!}Ot14ip!j8-gKyJ>{h z?s)KVm{`}eH^m?e;x6^$zAoCqNcRKjCA)(3@_lzB{cpmSyOI7K`Js06yAX|)LmJON zMaYxTUD@)k{T|(ZH_6#V6Wfkmt+9{NS_ieS{_GGdjEW=IgnQc$p+0G?O0dV_SO&_` z1cw+7PhVn9?}U)|5Z9^^HnunY95nyEYxoD-olN%~W7#Pj2@RROo+~@LZZ<F064!V2 z(J36fI{%NaZx4v7O#eS~ZotgpHVh*&qXK$F1x>NmR5S(@6~z@yD!Xj@-9u&OmbsPY z1;;}3QaaYxOIE|cNhN7@EH#W>7z-7l)p5%*DVz0p!Am+BsLU|G&wFM--0%1M`$w6X zbKcAIKJW8B?{oP)-TQx+w)KrY{9G?p65#yvl>d5*_ZGx@3W!*MHtP(>1Dl@XReF7l z3P7som=Txy>yHMYcQaxpv`sigiMAQuQTi3m`Q?<m{MRg0Un^jbPp&oIgng)mpk6J~ zxymos9;-EG#uKUjz77c$9@PE%Fv=MV2RWMHuEKUC_3EO$*as@>lW@~Xz5R3cx|r~* zECPi%rSc*UfusIz(s7<R0EdZcY!9A4$YUbrWO=aPg*)-?1-1X?iaJ<VEOLE9YVA9I z-RYv{HkAc%sE98GH)?Rzly*Ovvhv^Rp#rTa5)mGM3Nz+)UhU8yFTmt>V=hCY(tM;Q zH6*;W)W2pA<{OgZ;`(s1hh%+Ffe?FQFxiLZ?w>n|d@aWMU(m|F--B}$WAISt9C>*g z<|rQYLlKy5U)8oaOMM}#lOB=x&|}L*WK7gVRw0yHoDufroe(!Dz5GG*STbGub)fbn zXI$5!4krTriob{YmBYLDE53Izcc*%>`-4W~EWb|fz_-F|<u^7NW`IkGzBV&%4>4Mp z@7XSN=b{lSqGTjg-!xZc8`(;{i2KHX#>&j@G?o>=dSVB!Ct{rc0|bV=@@cd+VlE(T zO*0TP9~kPL^gP(4b3%kf1ObHDf8>~+LPV3stbL`@V7lG#!`6R$qUItl`1<+<XAcf_ z=2MVzBbH&k<&RY@PQ2sG*UHN8uDT@bD^^){u}b5r%W@)uX|J;M;s*@ENgZ`rX5p1x zv8F9<9p%wdrb^Snv&}=@lb%Oo*MH-RmJvfGBmeG0e3vFD5pJS_EjCLqj>ao4>MjvJ zlskOpEs$zNBB;wVI;?2*RdR3c#=N~HpU>R0l-#dy&IB(BJ4k%QCIf=9@5&_QvR^fc zJj-A=;|L%_QLbPp%B|oGuLOtEsXx4Q&rlQ$`h6!%&xu{+Z1+{6RQWH@Gj6CmA-A2> zo*<q4{jRR8Jk_P$$hQ;j(QcGi!^&P_ARvOq_4PNoqCTza^`cAI1E&)+56x?(!GuW> z>sLfr_jyS_sq+dgvgG|=My1?HP>yyRl{BMkRO0n}M<va>_rQ@g>KKCK`YE{i5#S6r zFV2ct8mzyyywy=>6nc7LLl$88dBjzqwOL|ZGbyaytUfFIJ%zZ634^0>!Qq-1G21^F zx9DEiJ@QW%DoBpj1f(cgsj9Grd}B-93wQmqJNRvj!20eczi}{5FzFIZ)WM6n^0^;z z+*rM7G+9yitln7mmHl3Wo;IP9&Yju0Lh>&@!s_3j#AW9A0nYeiI`Oh5nT6#E#N$)a zjGBW)S^MFn@^gZ+|97x3qMcQ5tCSO@&^QE4{}iN9mddD}<lfQ~VX8o%L}Pa(`%AY` z-dRaObAHhm5F^fVFT`C;SkcyW!m19r8T-B{5hk7L93dX|r!^v=!&&)c6c}gEC3c!> zC)&S`y?FA}yh#X_ta}<+3Ew3+>WZC*<|1D8y96QHYkB4SlemK~M*u?t+v6g4{6p5* zGe;OP-|`AxkjK#YSNtMwZ65h_C=N~U_lF%hS=+=K=f)mC#}!4z9>*5^IT`Lc-KwHT zlZk%_8X4+$&f$%f;}DYc3~wAXjyQ?nBA%kzanfGJ8s|i+q?Sg}^%?gZ@yb4b{YP*l z{&wNr3nAW<8+>F_3DLKCX}=Gogqi|f{LtLI9AuIV>7uA|Bmq>a%Mzw2|41-u2g6*- z;*gR59<_t#mj-ZGTlwxr8>49*N#{O<Q$p1F`7FxOH#T)nG;u!Ze(i4}uIx4PA2gO( zOpq7Gle_GMb#(nu>i8k9=#1{Jh*)lBZk;rLh%@H)N$CrpExAufrg-|MZ~Mv78y3sf z{afP+k3%D?YhYbZQLge<`3pZP1*tZY8yHP-@B90Y97ZN6KEfOC;0zKU=8cO6WwKsa z>KKLyxPJ~&NudVVIeoZr;BvbB1*T*jx&7=LNnG=o&LO`e6gf`>pRyx-^GT$*85bfi zn~>pae{Kz|DN4JqbUb3FYQj|YpwtfnY?`AU3R1Fqu%0MjaJeWQN);3!i0Lv|v+gk_ zeT);QWP#RQpo3h6sUG*0|B7|i8Ki;XcS(XFn?IZuJal*XN>L1#QEvKjKf?dgO&=(E zd<|#3NOBz*X{aAp0WvSLnaKIH%ZH)Zl9Rw6MRTHp?m!YmFgjknp^WE^TMe0`q-@@p zXdrhKaUAFrmzgJ8KsSBpmV(ieOA4aFeu^rMBxNU&3QGj(@t=}V=*Eo@h>sKGrQJ8B z5^<M>5amHq7`is4k`jhdLmVBy$r=B2R2a0J+!BL?m)<4^&eotcVqH+mqkHw5sOawz z^YSKN?yKr#LypR^(trER_m{2>IF5d21TNRnX5%N?g&>2*)#x{V8ABw2{85)^lIHqz zlPN;KbDS}%?#8oB(1rr$Fup^a4UGII1|x4!2cx`k?_fmjy@Pq%$3NU{Fv8pNI$y|Y zB5uEp-GylQL8@rB&nLDzZ*p%XCqPVBtbALugU^NV2KvKWJaHJBE6v;ak6|lMvqgMb z+>@@q-cgJV_G?G+V|4x1jtwn{P_>fpTh#9_so!7d5GItK@3_l2<{=U$Fvu~hPwrrK zXf8E;^bKEq4tPgC?koKT|BJ_c{5<vESKe9z9cWe1W|dcVirxP1u=*5Oe_LUKF_E~1 zxTA2dQ{d7Z{`tLfGy?EDZ>yQT<)yw%_=911y?c&Jf4S$pTcMtJknk$=1O>WcO;Bcb zKkv?8=(I72%m3rN3-CmwhTqVwGOlX1F>%O@CtNRH)&vhD2crqb3-up00h?oGx4jDu z|Ap}HPxg3Wgt~FX)qY;9o;^2SAfv%n2@XeG=p&1y8sHWXs2LFG?>=(0ccbfA<u5+- zgT&f*Teay}h~I0aAqNM`CPTbRxZ!Zg^2COOxVs{MJqj@6tZ4;0;7$l`qAIJ}<9wg$ zmwMtT-^7a#tF#I|CvJzJ=R(h+yAzv896>OC;Qai7ggb=<1X*|)T$GZCN}dyo<58a3 zT`{^X`O$x;oi=lx^k>W@Gw84+$6d%T_5T^WlGknQ$_J1Qonx1ayNz9%*nRBM`;Z-- zV;BGI`$Zl5lt=&@+%o|rnpl}wp(o;bulvZI!kQB+O}%#lH@Z*YZRt@mfz=KY!5N$Y z>}hFbgtEk50TrH@EFMHaTTAU%wZ=1%?()m0481+&-m~Xwjc<qFKv>(m!&J8&pFZF% zUdi+$e?nDP*Yn2HM9(DaqCBe3tqJin8mQl+)XANnKkRDQp2v_$M2@%gOq9h{_X0A! zYBbPMA=Q<pFf0iqO3<B@YwXl+l<8eug;J*|f=WbaOwiz97J{Y*sW3qr1QFU5MW%cn z+zE&xlcy!1FI9@FQDn+De5al*zrh>xx*TBnzOqe3Pu)|%1Xb1Z^Y7pN1&PTyKR-?O zwf|>$?04PCJ5|c|so}BwMb(@X6swtIl`LQ8ND|u&mia?6?n-T$k7fZ?sUY1tD`t0I zt+Ag<@P=CBu5d!|M3vwzwZ^4dLh!9ayAfRU^C5yS#JJ(N3=-TDB6#{^pm*QyLPN~* z-9WS=M|q>U?f;<bMUWwQ=@gJp^QgQ-6~n~n`;?2LTsaXKweo<kG}wzWpEtI2vaB@P z*KIdEB@g$xVcW#PAC2`;&FJ0ak~A_witQ$slx<ydNgmO)8n7f>4O2c>SHqh_B96N# zEZ26Y?N~Y8m-!&*1;49GM{@sZU1=DiIta3e%(&MtAe(|)s!NQBdVl?$pcn^-4!O%v z1O1^1{KxfXaDv2jpTPRN6G;1y3G7rSfP1jFZX%XfUEF?TWk2JHWJ{b#BpXr$%DqVE zH$EO-2LsdA`$Ee)_y=AJ_X81=>?8Sceiym&tKzL+=c+z+i5K~hh=9KR7=Nak5Fr%v zhSLS-9vEC%6K3NmTaFsML$sG9G@$&{ebwfP3BJrZ5CH${oW<S=V(ZPjS2}rl*PJEu zy>phg2Ist-QfJF%m*1X{XtI^x+LSnD4{rTivsKZYPDHbG$A{{Dp;?JNd>_w`>Tdgx zO2Y6gFFxwK$Gjs4eB{a#dQFb=A#xC|1o+@|ErbsV&o0Gxt{e_LD{F~pTLN|r%H?~; z6|n(V<ixSvn~U-ZT1O7|A49=Kl?+AtBM1lm6j4|9cZ5$RyLy6ko6uFCny-QIH+A;+ zXP4iakeC8q1cs~eXfz=}qUut<`;OS+3-Mpj-o&dH2<f+WHKUPFz&ugtx7~GUF>Vv; zRYF{Q0cHjS%ej~c(RBfH$KO)hCHw(o5tB2AliDvi>z?i0(AY`OWB4a7v#z)pVKT{~ zogO2;j3bz2bIeDSwr2SSqQToLsPzFHdc@OEJa#O2BnQOhAeNn<1c*FlAdi7-@t6je zs$;D23{MPr@_dbY{ScS>ei_eve-hT?A5P{Ps9Ps{{AF5DoevMn<|@sG=b0aPD^YPt zY&p)=TRr6C-55)JqKmPV)*hX*S(OIjUscAsD`|ud<x0D72RsXP0sbcJD57T_;QkMF z;iNC5F5E)MSbyF)6H$@D{q$?Qx*S1u;e^Uvce^o<mCm=D#}Ew++6qx4@cvO=PT`TJ z*xAD6b`alNK|#<3Ij=?tT3cn$r7DIbNcRmTNMp{A2PrQ;jz|ej@u(h9sRsL;ePg#j zzYUHEM3QQi91V=J&?T=U0xy_gOs+}NcjPt;N2M<q2O|FF1HOuSU}OGQ!<zXf<Y(Vn zzWfS{{I3$LeN{M;z<^VQ^Q8_k3*r%KCol^H{=^RmJAZC|)6$i;<T5n{VbfY-fyn2D zZx^>YzW%KUSkqdlSodl88I-5ONg`!GjIQAkrJs$YyUEtmNV;3PXxxR5#t5G;ZW~=L z*F@^JnqLDp#`;HB6o)a5obaCVHw``mV0tUC0~$GBKL<lA&)qokuTaS0ze)#CAHS~k zYL{EJ%NqL_k3^ad1{2_I$6AXXyS=!rbQm=nsPPW_s>%xG+aAeRaa(%v5j(Cph}?IN zqh^oesz19vBi1!e;~Ez>NOZVfN1p4+9ztf>HP*?ZFvJ^`^80&5caP4{4wgZD+0k}@ zi_LY7BT1fBBwmOC#r#}JDF}9z6oGqETd$$Yer1)^7~uheb`aEECCZ<#E!MJv2;0xB zA5lNzGo&eG{xE0GY`$bs^)n-nx$JWe+de^R7-{sXMtDf9@I9$T4j?inm*@np{xjmJ zrNu-|BX{OqEQQHYx9rDCr$uMls~ql8#ZBHL9-~S<ffLoN3kU^6pxe3fsX``d?R(yS zv1T=m=FhPv5G5I>H=)b?Y*zfLhs_X&<jHXwcN_~!xF3#p(l?yAbBd8SBB9ZG`o)%P zNg^!y=UCgraqc+2?dkv_a`troYz;V8tF|OrZh$T(C+Ok|i0^6|PYdq}EKp>QKYgy5 z=WhlC&h&53tRc?iA;Mg+jobd&@o%l!j;zy_d3kQfk<*pp-X6e}_?%~8d=27-6~A{} z;ELWmEpQutQ0XOGw{EpYEMJs&WiybKuT1{sI*Ylsw77dMaX+=p#Y8RP8h70&XFP^D za&ve%PT=^*16|GibwBdcmX|XnTVnX8fbd>v5JQHn4-UZdz70PDdFwMci>(Y(xy2Sy zvS~9CgUOG=VELZ}0z!1Os}@Byk3-O%P}E_4Vm;kp6&9y%jz*5D%{=vV<%H*=WfKzD zOh^q`Q2hQ6nA9WB8Qp@lu|^q%9aVONRXEhPLM|f?Bc0eA{}Fp2GG%HslEMwvA}AGg zje;mJ%4*7q+;zCSP4^4y+}T?EqWmfHN&_-;<OgqfXw5CR)m}pEGw%Lldu$-Nl2q!Y zC}DLC4N!D+=Vg_S=-?Tq<}f)bA_$78c*@tL?XOJ}D)$6e<c!sY6#-<eIELFcN~jb^ zacdiz>B)P@3vl|2#PUdTc!1gR8_A$iron{y+3vm8nUk%CX)gB7?Fq@L=H3oR)}rSt zKNiPxZuT|uqGQ~fPDg(AK1YRK{EAnzY>PM6k+u-OMcQhjkH#YKzq2v7VcPayPt0fE z1gFjh%tsn(hVWt*O7A)i1p_tW`x-0z=3wos32|tV^fC5;#^uOwI4Goxf9JoC^{{W8 zGMZC+_w@El+Z2ey6OM|w;6qc$LyD_&5O1k*2;g1X3am4vAAkHb(%xsxc_xF`LISOf z)7p&m;Y-1yJn0`BfA<IJXrW|;1%?ZH`+yZenplw-_H3L@AJe#yEX>Sn_;Y<Z$}W*2 zW|(Jj-w3NdrpHC~EGUjcw1yJH7WdWK=&0hJ_$0=#E`7|ab7RVGvi4mucn?oRk?+vl z;`ZHphwlCob)#^zQJ%PVM6))Xl!JHcV~U#ZAXhAP9x^g0<*{p}7ee>n6q?EGyU!Yy zx;H#?LF)Z#w*)<btX~F_Ch7jeNd+PDjmpn8Gy>gwacj5YzeH~BAo7dK?RY*rTff=u z(8uf%7}&_xPDf52Qb^sn#*koE_aiL`no8FJdmr{%ni9Vu=|7|&E3LQDT>s7CYaa_e zu!FPNONv@JC(RYD;atdi-$vjJ4!DtOtG2a+YfOCgQuO}k1)L$*fnkkAP|?@!q4nv# z`hS>ls&{VYSN*@4KB4yr<maC1eV=-j4(6@Ne`$BodKbydMHZ>!Z_9uyg?m$Q535?2 z7~vxVS*&iye+U+GAv`zoD(C0L-RcjJVA(`1i6Ao$&B6S2NseZf8yT|CJe%UE$WY^X z5dkt*`6ELAf;V|&6@Q~V(_TorZhaY8Z%9H%;<I4dttLK;#g~ww*!805$5Gq$;!onA z+s)tRUPl(g-L}QNd)gLfw__r~b;4*cme!Y%<}LO2b!(_Y{!_a-nCk4P7~a*JG`V|I z*Q6)gQIH+(9^xx~lFzEW=_*h)ZF91%B4howTH_^_Tz$5|lube*h`|JEtN8Cg?p=NR z>GuCna;2jroR<^Jpbp__xH{qM2m>%)w~!q9iT3mJ#}G|ZaKpx6+6}`!tP2l!(MHX) zxr{Te(>!ASNh*ZIMSbI+y*bP!a+s3Vk?0Z2rsw~_HFNS0fztD2E+|l0etW+mC#1=} z#uN4Y-JwL`X4PpRI1x(ecY%5y2wj~Qfa&dz;oGi<lz#;SBe6KDb|)7Q?T(i>ws#+s zI0<7i|L7#~^%xPkVZF%}t-hn)A&D+|3*hx?_h!O+#BOz!WZGmyE9>eCdxTsaFz(Jh zroK1KR^GD7po8H8xcNloe|N||ZOrK2hH^AeN~n1h_SE1tMt<lLX;q~MihoRuhW~nz zvh2>xz17viqBN4JD2mZzv}b0f`wr_P`=@Br2cH6D{_m`^Et(9(gTURi%4;c8!?UMG z$m632gP}3Tx>mN<Wp{0ZB3QfoCZKEpF;9%5gzkkEy&g&FpDGs>w6Tw*Je4Zv7u;eW zLGD&6l`2~bZj-;^r%II{;G^+y>?0|Osq(<Wik^?8%t@7#3M<Tbpfnv17_c^l6%mi5 z3`v#43oD`@Nm-aG0`IKX^^6M52>tJ5fQ1!<9!W_`l`j_DL2oam%0HLh4ir{!kEEPV zl}~0@@DpsOfcR2aG2oGufvIwxwPN5(+t-M+EUbuEU;LY`qQ@rN-qK5ff_AjfH&y-s z87Pj5KHF^{=AJ`-SloWwd%P#$s?gRVX=~dp{^!66A4@XAn%np@fnWSA(P(?O@jnE- z6(-cMF8Wg||6O2M2Rk&QwrJz;sJ99FBX71WT<*~>2p;?X69Gh1?9?2evfFCVm>N?z zi;o(b7ywFFamvl87XV513&puu>g<oZt&x=J;Ni`Y6ML1iRBi3e!+$>lyfUntux@|N zPXgOGu1v#;*67CbRonr1428jGS-@pO!>C~hD~3Mzj|=Z#&h_t07Y_%JimU`s?GgJ^ zdIjMrlF*(Dwn2WGxDX#@UAqt$T4%Q&5VXHkv-XDdXB*dB%@a+Pr#7c}g>@wX66D@L z+PHz{tVjr^*ApOI$O&k?gc0n3yQ)4C1Qt`z^!fI+1=Z}(ob}Vjt+%r4f&g@>o6~2r zYpXPiJ`<+M<{mEg9Vff4iVrI8N@DwRnnRN%rD(c_=+Z$&VebKTix6I$ZU3*-eGwV< zw^D~QnGSFRvSj!1sSogE@1=eokzMgOoOJ2gIJq*&Hc<78MCByvurz6e{131Q0AUwK zZC#ZzF<hE+C%E@!QRY1P1ng_g_}!)D&3UIA*EA~4s4o<JPkAQzB-&d}rQcD$36Lin z)xr;x%LH9YByXN_8s#jM-aTrV$z_QCiRv+_ApOqdJxw%}gq~{ITtWW6t3`SC9YnXP z?SV{!!OC3~n8pKeiX9E?)cDly(g#UL@WGfE$j+uRvcoTGTv5E6yV`=mHLeo|P2K|^ zXsL!uhp<xIT}_Km-YNYx@OYWYiTe!S196SV0K+MBJua8M?-Vjcu17`z@1?`M7G1W5 zMF=EWuROW#?)-(|{F}O(R37P?KXg>h9^5l`$y5)7#{8XFCFkxO1~v*9pLaeFXfrW@ z>Zach6QYG7Lx0`qu|&8c`5FzOsC-0i@fZ*u<-#u73!{`T0z&Vdnrtn6&6TL^81@1$ zDp6!@)|8!XO2U<`dhgMbDHZ?`iMLU{*Uf~vEm00j^clx5zp&mogO?cHIZqVjwM4J| z2H+>3{|4X9@>f`E0V9mUBCtwr*JQ-WcXESgDds&&bOt8lMx^oudZ-+*CS^^T;x%I3 zv`$9rv_wJswL}Y}Y;hV}0V93c)sCDSA`0P&2DvJr0@AG0t|@y<Q@uAMoJVu3Q5HqK zW*l9<W{=mPakDxg<=PBkRzr9ptM{;4k0r`&iFR3HYy}b40&Stih^eIHP@Y%@;JNNt zK=`4WmOF=*FQML(`Ei>j3Z^)#Cfb^#^=e|gIXX{lgeOOE$08U-eKOJ(tF`4sSy4bD zALY2LIniE?(VG(^k5)&EwN5{+IItgfYHV8G%TkqFypzH`290M@gxjDAwlWF$8?0lJ z&dPGuNrKnFcqc`A42+PEx?Y}1Q4RxsjJ6tBVHD|NQjGFIH~zqZvkh4IJ2iz`-jzsK z&R8>}iW9FIQ^rF?nPh+xo<UJt@qHtFQrg#L@r*h%vOT3?cZAI=^``#w6P)(;6LrYr zz*X^RBw!9)Ptrk{gP<rN``Vl+{dZ_zqsq-yY)DK<7}bJuH{sSv5psIBF=cL*H~xQ~ zRR(r@*4&`14?UZ;%7Vg?{?4ZFY)P3Eo-rxHGfA5b;!F~x{@vaawo2>%*FJ(TStn_w zzUt#-zg5etN&4toGHi#IANswe1y$9<Gx8$bdD?@Os9@hId&Hz}eWW*tEAQ^p^air4 zq>WuHDfR01tk5965lH(?NLLAi9xAu2dF<HE;Aq?^3B*#U3M77Id|sZ6r{qIjT`CEk z<4hkVmXJ2q2HUuZHhv7X03x_i8j>)H2BeVxarbpYa1@wl=4lkh-l?gfQ?N91D<L7F zeAgJCci>jNR{HYpE3u)0lXX3<yoPr?T7K2HmDj>9@=}E=3!sx^OpVB%ib6&~sf<<6 zx@GZB6|7Sc;CqK}4W7H^YGpy_{eK1Dx8Hpq_0NJYCy_S+YI_I8*FxvSOzEZ2`k9|I zN0XJYVr6z%TF7CMF)_k1QG4-QVH8XYXD4mUo~V=fJ2xPHqN%liLb*|sH1D_2IfeDT zBhL*GmKG9fg4|Qaa@B-=z?t^)-Hya%j!9bk0SgM0P12bkS2TC$FWga{4UI!c_+Qvh z>mj@sA$Q!qyBCC($zMS$s}TSOCiMA*<#uY^TK=qOx2N~A%I1uGoi{(+lOI90Z@zZz zf@4*PC1NF*2RjeA+o8q?Z?0CJ4$^P)$;xZ{FNtB#rF1@rHL%7RtWzT(bg>es@#meY zRerx+N>So!mC8X{B1`=V^<+qoZq{Brsq(m*d2kvq#mcs>ekE_Weosq}-Q{iJw0dGl zUA!&-+HJn(IdW0xS?nHRE@@WY)YYtVrTeq1Q8WAQ*}j^zD?Z)TuGrG;S=g9iZ4KI& zH$6K5Mf9erOR-bqMK}4Cfs{Y01VAa=ID;^X(DRR4MG3Y*cz2MP->t7f>i&GJt1s!_ z-TE@ml=ARw9S49!l|Zg-%g8*$HC^+N&USk~Q!7mf4TcE1{7S-3jyQv3QUul{B#O0S z^X(lc56_8p0F<m;X${oPWkMOJDz}h5nW$v{C+(CKU44o_bnkPJnDsyQ?{Xs$ma?<M z3bVsI`9k?N^b%=*l#=<M?<h;iJE3j6!!mT$Ux%*$-Ig70&5m#YOnH=YqTBm<%7g!7 zd<*Z6@1NbrS1tbGKl>72{r~qR9_!XugP8F@-hY|A-#Ih!i25{{*J<g#|9n<@F(k|6 zzuX<**WKEmBc|UwmyEpd_bZjTcb|53wLeoDc<-}oDy2DH56b@tHLKn&0Oo%vxZ#rn zM8aSfG%?kcHEf~UesR}p;+MhpEuniR&i@uACDe~g%cr-f`{BMW9u>dn)=%bYF)6T| z2Ztg-e5YpLL{kk-Hry1#sM6D2y(pg(5ui*`MF42EoWz9uCiDmkTDznuYBtUagJJU5 z>Y$Ie)Si7|qr)DM<3UkKs;6<XG|$fuPsxv%ov-!e>y9<AEw73s=%wOJBh0o{ktknc zR-WzZQU3IHDYQ8Bg=U2Jg>9&GJ?(%c>xcy>ohrGfZT@TJDu``J>cWYUwuv#*?7zDw zYF!fr=ftQWD>^1Zv2#xhS9W&I8Fl8uIDuLGyD(5ldBwryt#~Ya^6(Q8TwZzMOv)0D z-wI(=&)1-^MBd)Vbd!hYp@c%veruspNYx+QQ_L4f90>pT5Lc$<r0r2_v&(dx@^KVK zS`f)u1a9VZ8@DVkd;1m+hw%0Ut}KdEDxy-xFS8x`1@+Z~_v&E+H!1~|qzrpt#Mbb# zXij!S;p6fM4wX4pug;hldTtE8&&8F+aEd)DS>OisO`)k2S07|zhwK1jX!FHXj0O+I za?*!U$&u<q%YyeK@P+nX)I}Zca;cly6dkU0#+e;udQN;lxULpzpN%Vv<HW5|AW~j7 z22Tcu@f$d0a}*j1=NhVk+c;u#c*<nTflBeH7!P3o1;ty>m&J4Pe-jd<G{gr72Rd0@ zmrIfskXDVH@@~}faBg)9#*e4A>tUQ!O$_ATmro~$afAA)QwzO~yHoe~dvMD7s14!V z)@~$A;N*7*$@YX1l2un{OzvE#Y<pk=I_=3x>!K!X<jN8`Wo^_;VceqGg*-LkJ+92e zDQlt@^|jK}1WeD&DXU52*|}6<HnsJ=j7cHd!^SFX9HEcuZ_^2%P0;iG9e2&FWPHF; zrs<Lunvw^H!K#>k$5DQ9PcM>7OjB1`>1E_B3W+47gQkTc3bez4wrt#bf_6Rx!*|Ue zXivR4R|sFadaX5KnYu`0rf6Xxw>q*`rwmGmxwMYi)i6a1P|0005lTqQwam5#Bs<kG z+h!t=ke2_!tZhKD)0fQJnZ&A6y!8`uC}5l>>uj2uokP9FWzd?z;f?>5OH+gTX3(fs zk192(2f|7+YIVcmh(bk}aTG-o{Q;&GJnLbf(wOQdpOD)Fa7-MCb!jwWbVwB<Gzjy= zn+>U-#Q==c%%%Pze8lJ6h(FLH0PTUk$uJ0=2rOVmHeEbmsEuZ@WLFjjVMgT2Uyry$ zK0h}sB2_e86|SmUVECXcwsV1WUwcL&&|}d?_wlH>^o16UJdMrQ0>Z^QD0aj}fCb8P zE+LX{Pz|em${6#)g{{gbjIkli#>N!))auHOTXoJue&c9e@j%XXsKdBRE83x`rd5~q z;b8G9OX8egJt3?WZwHFwCS^j}%9VIMJhM{XPMUL6j54W6cmQFsXqsv17x;k4W8x0x z%a+VaQD(ke)C|mQ^4M4*-DS~;KjV?&zCWmq$;__RnU#uz*_DW~cRmRBiE;){k<V<f zedW8*XD;|z`W~M#yC*QaZjVX-!q?a*v#7~8Mj{(t-L1Vw3OU;{NSMbpA>GQw3S)<^ zO0tfRm7Zcr-X<U=)x1q{i-F)t(!x}dE}5v@zMU~X$ScGi34ht}c6jCfPk))%pC<mY zyE>bNQs#E_PaL=>8TO;7visP^M@MM;kQ9u%Cz;xfF^9K!N38zUQ%;>d8FS%-h!Jae z!WtuVw}OTo!XpIntlJ(4Cz!ISn+Kb>cHaZi1~9jItMoS0*kY~(WYT)@ytz_Z*TwS( zLp*PJk{LtWXdy)(kI-U1joM&f%Ho*ywix@sq@<*qB#cpBOJ38;Z!wLFmZxwfDKX@S zM8wXoCO;&KtC+^bq@;hDx8kd})Si`6@@!u`OB%rt7(9d_RZ4G?ANqxUNMD53OYuYc zB6$UVu$EK7H_}(*jW@{;t3@l*n3tutvs~nCOjL<A38gqueX!T{eTX{!E|9eTUsWs$ zqo}e*D?|xqRPcxpcwt=Sib}U9KPY`)spwr8PEp1z%2!6cUY1sj!V%LRiD`R}TfZsE zmQRgAl`8U1@zx_DenGN<MlrK5XoEJc^zGJ)Q7iQ$Y!#!0^rNwx^iP2m?G9vBg*z)a z>_y}AI(G#R8>w+gl&2!>g6)VaHwKLe@~DgT*8<>fUU`)<&Jn=g3m9XI&Rm#c(cJp( zdK+65h=NO?sFGyErU_<o;KkhG)L=XayE`5o6@lIL5(9W=JY_F=*m|U{C_F+)OCjwm z_g}1HrNNhAeigRjc|13YHxH8M;dySyclPaoh9h`FCZ*3sXT$9x({BhvsQ@+-qJ24e z7t0?}-xKFCyQ-nOKhNySJMvxaVYX;30Ih)qWxnWcxK&j1D|rhjG3r~Nun6}yJFwr- zt}{1APQRGDFYsbZ%&Q-QWd#$8mc#Wz^QVxKj>RyG=8IPvybSdL?fqxR^-U{m7LB7~ zG}*Umb7gG($ARKRH)#zyr#&wME1-B0`#~80JL)puvS~D#w{~w{QF?={F*~c8o$tuP zVjvQLb<SGonDv?}>QWy><jRHAkxLz}jmFhsPM6J?M!=vV-LqbI%~~w16aFGh6;cHQ zP9GeuHv7K1zp+-=`_@^DT#e^89f)(!dU->C&N1uN^{Wub;F-0+9!?2y06vo5jm_3T zJ~zQbIdt~|1o(?RQNhKtUC%koqW1&0@J+^nvWEX?BPL`b8(hf?<r{Y#vljQ)MhCZR zL;a#g*R0n|n*%~$BpPMIck1vuEGQg{rt2>T=JQ^B_d!@Q>z=ig=P7)!@mH_1o>@yv z>ENdue%3o{X?F5bq{h_$7_i#+uhb(|g?T}T)B;;B2M$0$jI2iu6TTT9KQi}x6m+1R zvzDOXLu8<E99=jrvT$5{ChbgKA_&_i_czhGGZ+t}x!|Pv=}cqq`T}Nf6xh-{1;G%2 zoBEtlzKF@NuWL*!eS}S~Mu_zZl;a(!ERJbR<fnrkSm#LB2~Eb70dN6@1%%O#6B<Xu z0I{+SQxydRyalFmDaycqjsF#VK7{`<M!qiKWN3_+6sRWZ5%AVs$*&&XO7oHI=)KaC zUrP@J5R8gFxbZS7`F>8C*>Ks@;5QE*qdhxjMed%^&q-Hc`u{ZeLF$JO^_6%Q0jpy4 zl@GLIv@k3m^_%Z2d1wV_n)`OZ#h{1=gR&0P!Ptg_uHYa@@g-MXOm*esd&LCxwcHy4 za}EZ8T4t`~1?GNa#meFxb%s5u9I{UA_%i_|&x9!w`O~~-0KSz!iqEhl&9@{2AFnov z`lXC<mr?F{CG~Fx!2PZ7(Dh3gH|WD?9Qo-shPLGovofp~qXC|Kz+14z0}F2cU=jkA zKg7X=Ir4|vm~dPEeZ*n-{q_Tbqv?JGa?TaE#N!+D;_aDq?sBGmudqv{`Y3r*e0>RE z>AKPNrugawXBn-W?<ieJ*hB1hBX_pSCmF5$)7YO)(WaBca1$eBj^XT?D{>zvzk)(; z2FZ{t%DqvIv8NjmFocyNaS<5Z+C=_Snrwrskyo`Y4TV3!2@mCSXaBe&ONFSs&|zgX zjR$~win*YK*chc>U<{i(A^@BNlUcwE(laq;Rv3vsN3N*fjDeU=l3x@lrv3~Wft1G7 zCkNbTy0gg>?RLcXKKc6g(RArjl8?UqOFDNktkj4f*%KwqBo)}w&Fjo>OUvMC&V7Z@ zp)oP{FkOt?qpwNqCul<oZ4oD6|Cw|t0N}zf7Wt`|tJ?ZQ<Z&ezws7QcA(r)Pu2d87 zoC#niUj8?my(8=Ar1dC$@be1y)Zgmwbb#idQ6csogK4sen1K_ALdo?lv0G<UuUb8$ zhI8kQb2LmroS~NqCma`uZ2<p;c~C$LqX2@xXI7z`iF7f-T{`PXNpnD|4sRF^#*-Gs zyOO61k&fiT#qH@)*1rXY{+2ZK){0j?O`v*ZQRm7`%_x6+Av*kmHlg;LB-gCzW8jX( zj&wFoaS7w3SK@PzckF(9Xh~xXe=DF4nFSs0c88${5nP+2$l7vlcr#}ey2!vM%Jae6 zF~SsVdSM(kfb-Zy>81GkFFQaed&NfakJ0=W<Zn<_gwWa7A@97iHZivkZ7LTJpU*wg z;bt^0Bip|hUD}U+5kU?`EPtp2TaNUpF2mWIo`M}2fJWT76fnHveSy+j@UKEGSa-(+ zT*c=+cICuCPI%C(;^<o~)SYH;ulrG2nwZ@zu^04o=>*ylnI$z{_y8$``rJ=CycSA& z$Scf6tS7jvI6ilMM<J23rGXA%f!7jOXfe1gF&;~-!=m?E;^A+=%<5NmK>Q1p4v1Yw zQ)r2WjLoRi7fo6!U8jY$$X8|34$8CQb2oOll3#W~le>nmfxd<=o{lelwS&+}H$@=7 z%p*kGGl(-kAwLz*uk0u|z-~z6@*_|1oVTauQO+Gn6&CF9S_}wLvRUFVlWAvvp9Uv_ zrN`MF79i|`qQR&qY0nk@)eZ~~4Jwa&>z@P$-kx+$dO|mBFlW{w@Uh^Id97s#r*8j; zp^HBfU&ET$svqadMe#5m2<nGrk5?));_+u;UWKz^ne*5%N5jL)r1NMPfIvj1lkeo| zWa!8(nyy0;7$&1G30)K--w?S^g}QXXr5-;R51$!YT3vHd^V})g8aC@nlBbAOF4Lv= zlL6a|0E~6aTDpEcTVL2=DtBS8G%j;Bj&n9y+)d-ffdNOs4*T_+9{csSv2<wzo%vt< z7(zs6U2Ky^p5U!d>9CQ#uW|H$XmRt4tCNmb#_V3b(fZs&r5U6HK<7qy`#-dfwa!}l zVj$-Fhb!tIB&?_m@bZQZAYI{afL%EzU6nUqsTavl(ok=1Bzaf7-&>m0VcI(koV6?C zP;cbYjKd_Q*7HP?NRdo^Pa?k3%8}$a;ORa0aZ<RqzQaG?662^NX>h_q8<tt?btb>+ z&;UW2u8-_+Y2x>r=~7*XM`PUIo6e0unFDL`0xMXH3xZcZedkH7X{+(lXj9JlMKqs6 z&nLL_m@?wEGLbnb91BWPX=6uyR7VwjdnCS9@dM%8w08_)W_ctTW$YCp9mG=?Z5lkA z(u47(r~D^|LRxDO>sa5SLiNax-7&Qodp~dka~x?^F_JOp)d5FzmbcH{4Wz8bzTt-j zx)IDP5BHY-;K!au_v-2Fgg}0LQR%<@ZsVe>VbDmhe~6>a5?lH@j#{PtMro6uOynW# zJ-OpX{a!!T4ivObx>3K|PhQ75PX4vN$`9(>_+M2er@qmTuRBlU;86^*OymNogQ&!M z`2&8;(s68>^H`x2=T${e_N=A0*d6ZJoo<VM{Sk{gj5wDiHusC}BPPd0!H#ZiqhV*k zPAM#)YbaX#kRx`7Gj^xszft-T>7gmg1zM}!$c&fhV@hC;%0bQ_1CGhpU#yM+!m@n! zM*YU%1g@(SDBboSUCN(c%UuIDp_EUrLA8@Uy=FgR!Px5m;`iJ|^2Ukcerj?39P*&e zxOiO{IC#A(&2m@yhy9I}Hr@g)38cy`3`<Ik=b!eYlIh=Y@4E$ce?UBr%DwnoNr*3f z%-<AU=4MSfLfHI*9W}VDC{gjy%y`<ygpou%hBo&F-@f>$MIags1Y3Q+KW%TpNu2wR zuWx40q#a-15e@NG+?u^802CMh0nzAZEp>u#|LiyIMdMA;OSE!Cym8RWIJtC<#$EGC zQqzulUgNf3A|WAHQwuFIXuOFn{ebeG33wQd=Z{xPGlI0bqHfXB0<zfPsnLg&9=D;R zS^R=l`U}es#P=911YT_A7RQ-@ZRV2PQ9;ryX%2%fy#9d22}ZN1TrH&&uKs%%xpP-r z%LZ~@`-bYuHd~~d37%@uR9rKMgR&m`dT-)gJ|_o7Kff(;xHEYH=C+<l+c^GZb!RI5 z@ATIW5PH^OtBiFcDEGA0W1-NH`F?n%Yt<$EcoALt4A5G0i#wpd>;2|m0WOLI@$M$E zm)x`#?66wEDho=~F|T}xE)_K5z7FM!j%F?R)lnA*b$N;`#OGdC^JJtxcSt}qoqGpa z6R=H9>A+#<v43{<oVBId9hr500jh;+0EPDX`R<42?wd`(i;~5Tde-kwe!VdHHFqR~ zdyBj2c9Gv0ynnpj%((<gHu&q?edeoPhQ_Lt1Y+-8L$&t{HFWMp5@$>9DemOgkis)Y zn|s{nOkNBC{B2gAvf0~+11Oi0Z`>;V&WHR2oQ@;r(4{ARH4MluG&&kG<m-mAVVqO@ zh0DI?@cTNqw%TL=@N9FfG5?87ru3-K%Tw2XV~akz|87sx08RZVpLr&-q)%vEO%vTs zg`%AtLh8jrcmrJIJT^_dY=B%-)efR@wBVC5bn_^bE*a{#`OInLdeh9vSsnF<e2|;? zXd;T-Lml=1@Ck?bN}tRAZxXw-3(To6w8XiS7r0@Qjw>zqy?#=*KyT#>{2L~)Kxu-o zLngScDN_8+ko&%GiuOb~K9V$#B352X=a%?zDcEr%cd5@~-v>tG7yB?yua5ItV)%tV z`3=rEbL2f#-^S&B=rfNg%iui6ZWg^A1AWiNL?DzRsO=LSF+i>#D7lMrC<t1%G8}Ay zxCNA4BT?pFMrUQhuw2&<;&09RgLtO4=$&k6kv4|Dz6FrpmJ0{zQciRBaxGa+Mngi{ zO>bMrX{1wzvf<q8_$Ys85PH`8v6$yo6!2?APa00-{pWs&#>Y>Je}jh?cTEuPUey@C zN>%u+iUw(gF=wA#fk&ogEvR9ofgRYa#<xa!PzY(rsN%;+xy3XW7I3gq+%bl$AM1l| z0|^aX3|CuAj4IjURMB1kbBFcVZ$;lj{ncE^c-1&HEQJYkGLc?JurVA+Lv4vAF;eQZ zk$h=mc%n9MiCv$9zjZJTSs88c8TD6ONRmK{D@UzFzh+i`)Gu{(a#C7t>KK#9qDdnJ zvDrY7dP0yjgdkrVa`SxTVjGcSf4lK^iX~<~)IbbAP5eh|=`h0Q_Hwo;5%K&IOt_h2 zgr~gr53Tk!I3x-ui`L00-pPGElat(&lU<Yh1viiLSdwywYD}z8_H}Qsy0x^I&t^Zj z$)<%g;%5cSed}4D!+v2q7>M>^DTE&(h@5qC@;@)ZWEEjQzChP}eA^1hXL%{)1^=5W zoPwRmuX2!?7utmeSo5?U^?difq`j@Bv=6r8u4`bu0qPOCmV3)|2+ngfraK$&cQuYe z^)30G-+$@FxucjdTKm6nkzsOwZpQ#aiio_|05L7U(vgokIpmt00nLFZ>O&u}7CN%j zQ*_77qM6eX%`%RLusS__m)6Ui^DvedNv`j?;;DLT-(GDO1+WDm<%LKgN;!7k##s7K z0_2M`c89X!ay_aeB`<I=1*Ga;P65g%HnYN57tUm&hfZ@%AAv}WYl!5-;{c_=akj7B zjTZ9h`n~NQv_6k6-P7I-P|%_1gM{P{XLGCCh2f>U+8yOrLf_q@b8Fgd$%_GEvyc>i zLov~k+a!SR{6~K_<sj~4E>bQW$Uxa{b<BEYPNiekUy*?EzQI?y2bmVhufQ0A8iv}X zhfRZA+U3QI`!zFu4yggo<i9$S7kf@<Jl_xpgy)+{4F{WOdHbC)+R|n1rdNZt+CSd3 zO*mBgdb`zVH6HmUyI=>lLQNRdN5rFjVA#NO(ES~CJ@!q?flp76KQSQrmXOPl{3@uB zF?oPv@<7LAB<2gFC9l8Js-nEx%CBm7CclKGbqKY%-^r_Pl+J5cG1Qe!kdra8wxhI9 zAjsnuP40MHtZ_G0H>jN6QEx=VO%;L#q_w2b%3Q)G_McX`3wBiTQn_J%?2a2hv+(Zy ziLM98>`dVxpxewJ*pgqynPC19yN(<QZ{4adXb<{53gF0z1jLcm{<g6p(rTfK^n#6} zw*wSlIKtK=om{+^i?IW8>Ix6r2KM;%Yp9&8vvILrPPcwlZPBw@T#7xM*2M`Zw+mjY zZ3s$02B|yHnuwsh7OToc*i9%R94eE}RwixFe8IyZWH8Rl>4WkVWl{j`4`GUjFO2#l zybr{hVxgfLxGl&FV_X)Hf_7Rs;e9Z<!-8t_HU>Aauxv}XxUAL7(WZcx82F3zvF`C2 z_jtNW19NPG)uNG>pw>Nn2*2FVl|8@#AHGRNlYYSV)k@=Y;fUA#K!p@N9hv#b7kn;D zo!*NuzdY$994yl8jv#J40YaLkf;3$PcHJslJY{*7x$@Rgjg_NsowVjq*8w~6vrc_= zko-OXX;CYK6onR_LJRz_f#QMNcbuHC$Ps#Ci~j(5f&giYPjv9-;m?MvjiKdNZb_PJ z?jSg1uO#?5J^@;#d8{W#i>-1TT0JFm*U)1yUFM{)sO|wSg;Y~Gf^-A%-4H%nGc7mx zf0?BwBm(h#A<u^)L=tg`O;Pjb8t_W>-Xnsv0W(ZTrmZp^$`PQlGEXbZe_vaT5brAH zh(^TmtR+Mf4B7Jxa(ibP6&1By8IQVGLDwHc3%$iZ-^$JbK1l9oYAlk4;I*pwmn!<D zA=8n$a|?ps)72PuF~5TZ)q}cI0GBC5l#&1@_($*#g2z`HqBa?F)cEl}nGS;2nVy!8 zj9Dz5Gmo8I3FNXWMwzIt1*A+xG)$LU=r1cn1e9jq0+>D4@i&6=O@382(c2(-gXDes z7R0p<0K_n?#u-J2a)5{ZR?Q+*$3S9oNsMd|jyV}cwk%XqzuGUYu8PRRhA>Q|>IV!- zZA^Lm7E<mH!Jj5XUE<}(4K*56J905pbC~=uKrzZ=)j>8huDEz-oPkVU{1Af|#}HiM zppR4t!@OJ-y2JhMo3M+nPeNd_+O}A93kf}Ou?zVUD{FjBr<Nyg;^eo<`y<tMUFDY! zSCAm}3F*s7EHvSs!dsR<aKaA@`7#_6oW`l8JRG$jmiF<QX5HDQ+QS3s+$<D8xO5>c zD(};@&xI@EGy|T+Q<wjaR4->9l{*&Y=>~1@_X?nt_(5Ic$-f2Alek!c?lE^zXfVLL zz+i@Uz?|(azjo@~F`Y}0N1(L<Ca=P-d+ZA@yqf|RZPFe=(&?bSP?ewSVpGXZRx_;w zI60v0_Ihu?T@iWeU2o@ljYqd3;COkw!4nq?gj!?AuQ=;L(I!rMl?-7(2Y)bdgTOFU z6Lz*Dv2?%_RgqwGa6I93dA7e3W=|a5Q6EQn?QN$X5(ovvEHZ!4%AH5mMB@Kb2S*Yg zL;1xJE}8fyO$GyL29srTl=Q$Yz#iG+V#A*Muc}CO5CH4MeEW3NZwa8BlrXp84_r2Z zW~pIkuA4uRl7|@@D+$PU6DQ8}<NQP=nFqM^K}Q7fF~1vQ9jx~-Oyf5gVX#8Nu#Vi7 zB>y$B1La;)pA$meDD(WlTJO>IS)A|B{UxyeH`W+v0~5K)=8!k}bI%08zk`wE`WZ5U zv+bW7Z?O~fQ%>-V&Aw%mUhlA<-mX`xCLKBX%;KJe_3?+V6+7bimq}loges!cwUlzO zUr;9=S9R&6qbHTx7IMh}Kp$~FR%uM1<Csr}zClf}#k)8bW%q(MLF8u~1e5~AsQ6`z zliUpem6}$j3^WK+a3JcMHR5tVvKcT&VTjPnQ5IW=008^zHME}*nA<OXVOiO4@Ze~# z{q+g=6~Y0>DHCtQV-8hj=qRhTs<wJ>$(q`t@701l(qH{T3W6-6o%V(M`;DD|qCXE% zRZ*ZWMD*$VREd}aRx_swb0HBI`g5P4w)ZQXFo@30L}KqAg5S{*FSvw%-o~Ex+FxtK ze!8EUaNp%3N3<2mLi1L>P1o;4#xjg^^ZcgIH&m0Q!|8uSupR9v!uh`yXdm?p5bv>W zdj(xOlbSH-Z%AEVE4)osL?1i3lAv}J#wtAxBS|$rR+{7AG2T>NH2T5xchcXo6&i*6 zk5~{)aegt)U#GkUT((7UR78IQ@7mQcfM^Rd9ocm05X$>iY0)2%_-xh3*09HMlo!U1 z<R!^3Ifz&$$y};fmaZ@e$?S^AkhLg=4ELq&t93=s-5}J&ah1Ypa93Fkuk1~6`0W?8 z0}D)BizdaApaSV>zZnEYUe-^36rP<UVwvYI#|iMqjAsp=@7vr_l;g<6>zLA1N)|#> z5}$;HV?!?vRt<!eIF2UCiH0irfTbx60!8^zMQBJNDUiqp{NAryZM@Yd^9}<39U5cH zpv*hUM;hatL3?m59aKHs7KbX11Y~6TFx-(J@uwxbI2`>{(G^zSh)Sg}<<e;f71mI% zr(9pXQDi<x)&_(YrUnT;l0e=#Dztmjf<Y?6SSk7*l1R5v^Omm+i0^bYCsGDCljW!w z2%y-pqzWrYD(zLHI}n*NteQ=+f0i`Jx_bDi4D8BAGKC{?183RFP^A|IT>2~@$T_}R zw>qV$C2mK7O@F@~SG<vNq+DHwkWQ-g6=r~c^89YB!jc#_$6`+K5fGQV@XqNg(0DwB z4=5Wn7Lg;Kf8}k>87*<UE9*7J>S%MNvQA?h6>Zf&khLl%YgMiBwI~}sRky`>8xXaE z>1pILEuaKL>1e8M1P2_(#${x@1Fa1Xhqk0TrE%txh><+fYoJSS(X8J*ta#y1i`z`w zjd=zjjTu|wAnnM~A>8R$5;6}u;%Z1zvM?1X*xu%djK3IyY1k+*G}bT7#a?vSS5-RZ zPi9S@OIP9`@xUZzVX!+}<3c3lT=8<8JDzL1I!K7jn8A?@Yc5QELwf!ytW()Ea;nKG z>*m7o3+TYt1db0q*UXgS#0H1D=&a^2se&b;LmtfeTFitG^<H{vaUT>r5OkQXSWM*} zLa91HIOrdc&ik$Ol<fzy<d@W)kR|k1cY=Hnd2gYekS)x!>4yn@Na5+iVbTw6!n4-w zY<^|H>3B(AqxWk+(3YA5j_fH|-WGq2_DQXS&XN`?MHN3g^(fNS#5FTgiobqQARSv+ z9O6%h48*FhPJwR_myxX~8<4K@dFKX>Qa3QG+@Lo+z-3HgC+}3ozy>DO_)AflYLR@~ zhtfwRt_3kKe}`43iQNg$iKx9zEK2cK*UF;J2=;+f4U!A}31PYM)T|%a`S#CVTx6Z| zjEyg8eJhhB+50&x2rf3{8dJR=<s<BYCEDV|0S8c&j@)^!YBoH`S^1Kk8r3geMbe#? zs0scfIc1<Aj2MhkpAP$L+x4uF%KL~*5$_4Qt4QBhWV8i*d!n0J?Uz>uPYt>DcOg-} z59LsUcSNxt03Fr%rQ|edb$Av##tp>i2TvV{sERaCR+j2<JqG8D;*M9XC-r=2<w#r# z!<Zm~7Nm4ylc2^P3Rvh|_SfX^;?#DCRR-6p!3sqo6i3?ErpCQ2M=V|Dh}rfvh4v3W zV73e^w9vCH8r*_~uvw$If@(JHV-Kg<S!8@Wtho0}R@YYJs<>g9oUQD4*iljr-7;<o zbC$L4f4AtsFmt+88@IX05fSZb@Egq$Zoui*gzH+0u)8dR*7U^}u{)D@#2!el8^!HD zxW4w_dYB<Ti$j)w&-zEHBgy7`)0MNe3yvnA+V(yFFk%6>8ZYX|fjWOY4qIuH?TY-4 z9|(B}?En;upZ^1HRac7#J>sOPVpGBVY-;q!M}{5MD^~&sip=Sb$v9}F{iw2VM32yF z*_W%M=Hn(qQ(G*>y?WyD9hQZ9Q>AhVkK^GZdN_z?Dh?lF_i<ZCOsqg7hSi?50NN2m z%=XN%wRwua1#PT#B6e!TRUQ@yR;2x)Ptn`I3tHgBTJ0SK@?<<EZSg4=5c>(Dt8o`H zPr%HS+bx~80GacnB|zyo>>Wr;wvDHhb3i8~U5h5Q^oNt9!Y*f3X$}k%GEE2N%D4l= zoI18jGwookc&()<ul2|<yzUY>S1vW;QiOUO-uB@uM4OG*9KV$08BgVY6Ci_B+I&3n zks^wAdUsgFFWYo?ilV)ni;!dnS`1(v5iw^5?XZ|C#UFySsM1NBe5E?d1<INegLINk zb<&BLET#3nBQU&G;V>|#qr+@Uw#OYP60|N|6p%L>NpT)IDv<kW@GgQV!W5eApA*qP zgYRG173rtm5rT1gFuTwj#&AfQ2WCz-s;}ya(G>+wKZJEy?m&Jv**C#wXMtzM030dQ zfq%Q=668NCgSBfL@R7vVG}EHH4V#dmtZ{nbEpktvu2<%@IHvb7&grnlCDio+nnP<( zO;ZF?S6x5~LouA^TO6?zo@ib06)uag!URFi#Z!<>YD_WAG^nd>Ru(BcW1aehogU*S ztW{s&(ob7Cdffg@3#zlUz8o#!C;>njIdQXNNpxgyM|nGG3S6h80$y8<?PP_eq5SkW z+u%OSMo=EwAtOPm{GVG~vDAq^OpVrFv*PeUO%ptuxQrA~E?|#c4-~bqLT`c}hH9{S zy^s&EHqsj@MI8=6F@hsDJJwaGCq>Dl9Ob^w&imf&{Ow@p%91wWEmR^^Wkc7<rUri$ z7q<yhTx4XSdlfSqvC3n)mjD;qiSNKJdM3e;GOpC&$U?2uPn0zf^$vEClBnR35TwJu zNCQ~6jO=I#IFo#>-BFR&j`Twi1YwN8!RaK)kdn_x#U5!v8{!~?9g|pYwbty^mQ@;o zDNcAO+y3X^H~B3iK1)S6WoqyzBF`Es#b`agko)S>)0*jEbtoZGns^JS8^gcX3>|v2 ze=iqacG6LbNrCQELWYZFiB{Ax&I12g^%|R=-je)Q?4}1v*-L>b`+@x;1VT|=?b0nP zp3=)^dhST_eAApKg~R(bPlmntE%JcFpN$<H@meyU63S5BcntekUsuBJo@}ojXZIl1 z-5xmAdu9g9d@=0I@Un+ErCzs6gO9cZ&6Zee0{vEU4YN7<wOA`lZ-v>cwkk}D)E^i& zo8<)#4vxgynJc!H?(1(9`ekjeMM!{7Z!cVtHV*Y{C+xd&VAz#!RcI08C%ESJx;`NG zg$TirwcT37ReqdQSu30LYa2pUYcFC-_1^}P`ye_iw6-YaQ80qbuxno_@2OsMP%U@$ zo2Vg+){7?wkuJud207LdXKo+l_u?4QdF+7`C2Z|3yQ%SbrQNEf5XpZbfp%&c<JykK z(MicSiS(0ykG;M+wj`w%>T!t9eimf+#If!;+~jE#&$TktTN?Rs1?oc2P#pCcl!+Jm zCkPI`p$UBB%oe0_A_t`4qRBVUVFkYo@h)#I%R=m_Q1MDdpZPe_U$L5s7cF#zEkjsi zah`U5k2x#%YnFt+SaNgBbxS$3GJ_w?tlW#RX5qiG)Bw(Xvo&LA60QbbB|3z3Bap9^ zt_AjFaaKK3z1OLuP_*f@F!=fk$(aBq+5Lre@p;FwKD$5JYnPKvl}}d!phL6mL8T?I zw#B1o+<Hw*v#I%MAg>l`OfL&;K_5o<+ilc*lzs?ITV0EoMW<eKB8yuU>kqrAap-Av zea^O6hu+$F{rUpeH+}BF>qVXYW^e7+$;T_LrpBimW03!CYv^O&x_AA1$)+t&Z;4sI z{^sE?`q++jSn>7hz4&M=)RerLY+Cj7su+ZZZhEjcaEa26x$*Pqy?EVKs5$;^vO2Ki z$8Vncl52gKCb&<L?m}ApIdCTN7gEEBhxzgB#bJ$O0hC;bxO;UE3*1XTup2B~ik`Oi zMCh<K^#ENmfCRO^Qr>5_{j(5Gii2M^Z5pBbY~!3~oSGNtr;|YUm3lhmF)S|OAx8Fw zX9M{u$6B0-K#{U+L>^RDh*RQ{%hixUgi{OLo*d4h*Hn+Qu_Z5Pw~g%IGq(T0$z|g> zX`@c%-N8Ck@zm2U!!oyF>Hf??r$(oGj&*Dwg5y7SZG-F%G%k|L+Z)xCY@+!{o&X;A z>NRI{h2u(ck^eug1PoQH!WKO9(cj3IH&xw?T!9eJ{B1xO0DA-?VW+rqG`1Y0a|ZGj z9dT(ms;%Q$#|)GQ72kzbOzcGfepfCrc$kW?c~Ld0Ej}H`2rf(2z_gZZK|XaHDn!G( z#36A)o{(8msG0WYuo_NEZ)=^8s+>^9&A@;_u}4)RGOX~F+_w#7;&M7;w&81Po!_r` zQW!btOk$?e_2j&uc}-4g0}>T^KJ<`I8L6OG)F>Sz=)5E?TH}dJ^As?|*<xYcIU42u zIC8wmFpy~E7(kw2eL~}u`{L%}C=XSb;1pypl3}N4P&XSYXHpx?IGsZsEB0&?0Jhq7 zUUbS99Ko9n4g-$Xgf0Ib@hnHq+MDN44*Gz>g>M;3xp=NwyDbrjWns??WMU*jwx|u+ z+V~*0;XW!Y1^HB+nP!VuA<9x(Lg+b*68fNWb`@;T1gUL+;IzND`?Ff3X%J#Z>}zVj zsx>Z-ciY#VG8B01tBGkMsxJ;>`x~`IuZ16ohsD`8J>iRge-R5z()06m>NyF#k!Yz( zBaz_1plTuh40jIItTis;s-no)q$zE@fpRf`EA|y_>$Q;IIqdv=c-%F``D9&cqU99D zQQlcu^Wre+sD>zJ%AW{8Vkfz#ihP?e)sw>#Y>uiZ`Qo{dZ7)v05au?)nxpG+?E$ct zllrl<aIPaK63|A{Fj^oH<P{Xk#8--C6e$xQj>9I%Z!+w^TKw#wj4gcxt@sgToYRMd zr{hWEpajIHe@GcCdk9asa&Y@|#>LBF2-^_$S0kj0eB%9%9N0_Zj&=5m{<@93w|u8n z=*^!%*q&zRFaS0eT@i#uX#IW4n1{c_&6KeLFxiZ|>8qkKx+MF8oyp4HaNQl_U$m}x z8c1A+|G|1^&`r@{bHhxHL>$eag?X)WY0nJW9nYNl2j`u^z~dbDLKq3<yXl_6w7$T4 zW-!7Fc>mOwtQ#*!y5rf#Cxu=@Z_f<2_J-xuKWOg^)*H`wr%!TC&x3(8o_5AF(uaWq z$&Tr1c-l@L(lkfAr>7UjbA{6f7al6C;-s>GH7>1aB6h49wq$v{4`rr>V}fgmuKxYN zU|p_1fGt-%^0F`o#)Gw#F|k9Kqr62Kx1xffs%>ZcJRD30UJm80^zg>OhiBFyj&b@l zH^;j;4!3c~bbS?r@QLXK%2VyRD*{4hF6te^RH3W{&Div<${W;|4Z;`#fn5PhD*@ll zsc>Qj)2BP)8OQX8ZSkya`i$a<aTiavU5kQ+9G<?AbXfqre(V&p-rVPQ5cA!AXaK;h z9`e;c3yUY%0@XAHaZP}i2uCZTvV4<TZYQS@t|0(3o}Uj4W{4cKTpCzj5G)Oqgf2q6 zeuJ60h(eZ0HdKaM2jSPnz+=VJt&nA-969U`Ud-_E`2nEr?heGDAdY%gsJ@4Gl;+!H zh$c)fS7Rs37WH03+JQq=In#=~)6R`1?Kn3Wfu@wQ{~Z1`x_v=aUXE`=b^D;6(&^8b z0Q_d+&v+L>@6K``{1s6e-yCvJ6bzl;L~4u;1ZA6aBz@OPTolCyB7KxS7>GDB1nUE@ zM(1cw5{s=|c@8FP{6TtGBHJK+{tzI?f_eu)qO%GM=_t_Ha10bM#vQz~{IV)__&QS0 z=aF2fwRyyMz$)Lgc1Z=q(fxvM;Rq7$1Ku3cGDg~RG#(3Uot_7#y~KME+jJj9uyg?U zLih`=tvLu*hu%lb2g>_xs-VDZJ8&@#P(@pBlj%YRE}=0boKm<|vd+$7txSw}p|%aM ze$D&|xZ~JUiIc%7YSH3y6!MYVAq04gGmKE@tJW9osOsrDR0v!0`oJV0V+s-57N%IJ z7m4Fa5iu?lW>^0r!+--RCELQL54nUA%t%TgBuArCE46KAs7(YAcsb2&9*+&+0WC!a zOV*H#7pq8elnWphgG5|u5S-;L`wc@r&<@~BX(~NM=;=WF@KN0zh1`o!=VL;hUh3LG zZjyclwux1)Q8?zw@`q-$zuSxyYiLFUOZETgKW%3+MnIPF6P^s>ZnvTl-HIQP$;XGf zO-~_%8^lMEg;oi;yA|TFc#3)~>txqCa^mX9nM=;szv~5hNAfRN^!u>Vp6Pjb8%VxA z;i4oH=s1$Bc&MU5?dp2ME>w|9YFSqdP^<D1XDX?6F%yWZ1a?dQ+*hMjw~0Is{Dk|I zsXgdC0+&cvF-%z(sE;K$<`pD$rkMh)C}Hp*Bor&lsMX|VqTM-B%I?mll!w%&6yx2d z<cnm+ygK7hThjN`my1YKaBW?JtYQ^A+LCv5wjzxV4oH8u73myVE;>+pHN*hWEx@fS z4PybSO6Ipvi}`JTH!U=|tUx{Ri5Y3TPn{y%uYWjqcXmIM*#)cck|D)GL;EL%3IdoM zHa7+YEoM_I4l@^{+iwm7k2Nvk*>++T7Kc%43fK@TXb2XEQpTCL&6VO{%6R(LFCyoT ziwDpwiPpM`3QSSDYsPul`bo75pk^5YxgOQ^i5`J@`eh52omROUeah=waa;?AeG}8~ zo|;#~$o3ZGn(h*_w^G`gox|q-Q>8_Ajz9(`r>X;!`%!9c8YB0owyic-%6+MAi4G>J ze<BWPbrcGgwH6Zn051*+<o-P<V}tfQ<zU+e<^RsR3mW_6S6j)wFt`VyLCtONw2><N z+lsXear?ZautbTUV_lS+&6c#jYwEOkE+5Z14=*lx19?Ez`$F(eWx1SkTHA@K1Y@DD zt~Xa!%M!JIvbEt2B&1f3OWhn^l19`;NW~EH<xh*f{ps|Kaj0Gc?!9^k)_}{v8Ub(! z-0?YGx(56ID0}ySsLK3*{LGniW?<$3GXo9^gEHs=RJ>tjnI%Jn*Gi0NGix2U18G@l zZWptHvG5Xo7RT0Gx;u=VR8}^bT1|}>rVTH_F3wh_N$J$dL`|FuM46fI>p5pY-2D7L z`}>C(<~--Qyr1{;e%|*tgYeg9JD~~414{|K$#@Sz71+r!#^yB|6+-`9M4TME`{%vc z0Zv!5ZU1|4$Jy)o1sTxg`)7|&)-k2tzce*x7?qGS=l<EyizQIQs-PoH<qfIB)}h}r zYTOhg6|b8L6O*{qL=rPWh3z((y`}`Dj=yc(ZB}IeblTX}1v_oED)9@Vg_8TXPE&NV z*6ESY(@Yv2kzL2(JIUjz@T{uhsi~dMY9aC>>a6s;Ufz(z`-&N|dq4j}NM7TX@$*e1 zbLoWH^niJJj(HC_CRA%Ci2ZeCL^iEg3Dp#F!I#uj#b$g^RK>N_jAYDOdXFzEwP|Up ztB|OmxTDk9h7Vn>2P#J%fJ{r2%gP%5v)$MacY8ja%b%B<pr7TJxrD{jSkhV1($rMB zGj^8X>L@lYD^7iODgHb&j7rQ|qM@n0s?yiSK9iSP`n+N6loh%8GP0qMs*x=Gdzw95 zcG@&5Z8~xekQ>s(O?6D89MdT<LbT8wM(G?{x*VQFETZ_Bh@+1WoHMa2JZTR8!^cAW z*BPF)22JpR_oQDUase^clXBWIVgOmqxFqRf7%Ju4oVhVz(uge2#p7op&o?J!=f*%~ zgp37BOhE<tLHNg&^dxLw4ARQs9YmQb@BtBzh0oR`5(&9~#eSH7C?q|Q5&605(Ik3u zxe_|*+F;nQ!BE;|=t|U%giq;GWK-Las3SjYU`CRVlsh`)aLTv$cHX1u9NlkA$(;yg zN*sKT?ijAZ&5N-<P#swZo9K}PP&;^`NNrg9T=h6I+$&Yoo63m0cNn&BFjP)8Ou>jm z?T-<+C+f%#7%>d}L1e@&b)ENVI@7L=2%N7amWEw#($Mx7thS1U@J-}ucB*bOa^uTB zXEq{J$osBMjy0>H077V>3l_4+^+#dB`U`Fm`##%=<HyILgZmurs6+N0*35B7YL29p zrj`12)N}97#{vd$i4MAyO3tDFT~!)1oVv$=?<?Qb94g=jH7r;`9jxNCQ!LSE-egu4 zqz_baY;)l%JSu&01((oeiE5s_u^@eriepH2!lf0%n(uDT*!BIcn@?>?tvt2V4;)ek z|I&WQmV80qeAsrXb?I??5!Y3Y6H!z#VQVb7l2cn#VE|iYysS#zs1J9mDodBHHWqzz ziEQpIDmXLM&4AWHxY<;(!tI?3!^kf2Cio;9a7Zs8d@gD3!`v<@*R&SK^x=~ee~7C3 z2oW6VV75JtNgjJT7q_Hw*di4$8E<;+8QJy`+<2f1>$avlts`<94a-(9+~={6bl9_I zPo<_$%Vns7nJhPT&O@`=QX~_$4|lt$=XN}d&5xmya;W#JX1#6L@V3EW9S$U;oK>Z7 z7&dHFaVZtSoVN|6g|F(^AbQFhD$ZiIV2|;W*E$jnVOYn0?}nOtCgrhaRsNVdl7tZY zXCSSm&pN_u%^*<1904XDrWsqb<rk39t@@^)!j?NA#cNd919*1<DWvfxf8($~<7h5w z?M&8b=N<N80sG(}a?o=#z_3}<9oAvFR^ELt%oY@`iON-_4tv^jf4PI>lvU)sVOaUL z!D&qc22+lz%C`+GH<2kn$#~<akq?kY&MN4Kc0<m&j~ZE1DI33G@W22L>)_-c-PU1F zE00olfD!Mu594$z|5R?d{lNo>pe0qLG4D&*pt10)u*{S)O&mjlT8GXIR?TE{0!wpF zC3O5~ta%rwkQ$r=i!JqWm8-%~S^@7mz1My-mf*ABwE54G>76(D?c-Peseqf2T<x|e z^Lq?l>&-svO@1q!<I@Ay<c`foI7?E4a&G@a#~<D+bu3*&jwQ9NYWIG_-pzyD)}hJ! z5v=955A|Ay`Wwe0OJZOqrO{JowhTX6=(G;SRFn7Zu3YN04|Q#-%$-JoS}H%|Ur)3u z`G9AdO5cXrLjiay0ZXWP7b{TaU)1tb{wHuZziE5LSKN#l|Bxr?I`UqF*Xp$o;{Rg6 zR4>hBz1Be;?U7eNF0S+h;!3G+t2#o4u0L%HSkuW5l>FZxv81NY!XE2T%k)__4Q~6u zjsr&FZ9~1zW4EB5Bo<+=7|W$3pZD7nefA*%tHo;_;%nU9+Fa?io1I*#(>}mFB>Iu) zH=}c{=FNZ3Wy+lP%r$vPH96z<*Jr&B5Lq4eQDT+BV;$9T#d!N$Id8p34avcD7JgOf zv^wphVF+J#z^$hgJ2>U7YJlr2jCBk*ga4C3pXab<CO?4nxUHkHg{7}IfQ~FY;^C-e zHAwu{3I~9h6y>Rcv$%6QRW$Xo(~77-bHJMD;L1GKA#STB`K!(C{+Xp9U9IrJvJY|0 zEZYsA@wlG?)*%7gEVp$a03d8IByT3F)U{UgLwc`jma<9@_lkLSw9`J$V;}3ak9kDD ze|0YRs>7bLUs>ZHXZ`U#c^#?3Iv`6bwA;5=Qv-9TpQ>`I-&=SdJ{>N%bs4Ta%5AsS zGKzNxPOma?Zie&+tkGk&I;|;K<`pA=Q~AdR89x6DWQwL?jpIDlv0m$#{W~4ptL~Yv zz+Kca^Hr>b8!4?Zn3EWXYL>Hccjc-|XVj~Xs8_fdn^)CF-Ts@>-@G?#wsqm%!YTup ztYv>w1_aQt0T`2bdE`;b28{a<LH^*sE5n|~VYYkNQE*b`*N3+pf!A!~Fc3(;5s^{c zbjR_qBXct1$y}g03ntt4GteQZ@cfj~$vl^4&#tU|%$fI`c?nx^FUx7tT<<S&g{!_` z#=(`?o|OCXD{l2G&nJy<{@xS+0EG8XzOBRMVs<}~l(nB<iqQ8#Sref!`#rwJxvQ-n zYZ6g+yaH>HPc<TM#P|o-eUxU+hRMu2v}o?X6}lz-U_3e#CGdtpNr|*G+<hFAJA=xN zDNSyB?%g3EIwNby{`|qP^3Um@mmonMGQ9+`1f5K7?tRG#3Upw@+g=nf-5S@_g}8CI zeMr+{g{PD`19E?|^{U~vY2$9Qx1dVGaqD3tucuzHFVb=9xp~(6*wk-#I|ftFZydbC z=t(yo8hp5b?+I62*<t%&TnoFZ;7e<ShbAVnpC`QkFN;&*6*+cp=Hgl4uqvM^c4eDY z$q!_#W75oEr9dxN{_*>?>UU*6aXwAWeW+{}+kDeKL|)FNRi$~x>G?T1KXY{IlFHKO zU9%Ha$!Sym7|RWGCej=|c2k3#A_9EE2Iomd5CCt+X>i>u_L4PIK+7}Uibcj3>wYHJ zoijacL<S6JBr!zs`V+mf1K!cD{akj1TL}hXc2wzvKN{C=P-kb3+n|o$LTjH*AWf5> zf99E`#T$;n|Dp4Fjk!8RAXju#2nsdApVre=1uRz~)+0@eY;OTP!Qr*(5+5_xYU^nW zVp%S*LI*SiO)SWQXOyu;17o`Rgd7CW!hYF!N<&d2jQw)XBtA9>%pS?S{7V>@9~eRl z>4ZP8Djz^eqkqBWcHV7MrKu_CHAY(D8}f;f`_hf;T&40=S+*Zg)#_c9ODjKZ05Uoe z@N{7RzJoiW!782S-!tOcfvLcQ@=<4ro|vR>7Qk5(CYSJUWMB*bV|al|N$z}lHXDRv z6>eod(8q7vslGW1=lai4I<3MoPlCGekAK_Sy&J~hZ5JYg0A{`KTEsK`-{rqnHEF1m zuhp{RQ&+ue3K}Pr$dDtZiFTF$OwJ-m_Bx3GNBUNIxkXJ<O$aAo*}?n?KEw1=b{Z+V z;LK8-$)mKm9$9VPGmfkS2S~Ejs@*mkgB-iW|8S}CGzp8Fi8+yau;^C!y8oP{&#-aj zTFR$|D<*Oc114;nhHoOpc6<Mm4s>6?e`F5G#r?9bu|Qq7L7n*6IOhmsVz-eH`GrP@ zifY)I+W7+QE-@-Afir@<%?hrFEh>Zz^nAxntk;UKp^`A72sRk2V%5$JW8((7%2o+_ zOK3oEB}R{m+VfJ>(E@er@l;<4EON>WMNePOF9jx2OL(najZ8trg<zqPY=I0#lzvc9 zY$ic5=@S*4!J6Od?S}uWl3xvYp|ozkh8Oqu;)U4S4=-f+AH>?ZELiYyoDv_K?jB(z zM7e~N=!A*m4gP939^kP%!<cvlDV3<s<tzyqDKS=_AghRM@F)Gj!hfjJn5m*dmZz>X zkC2lf696qy0Q9=CE{=4!@tD$2;*~^ndwPR<&vF?!a_6VyzuINy)5YpjXH(A}>s_Y! zXL*^YH{xdlq;BBYo^-uD3yV-6cFkLw_?T&)d=h0a3GV?bAf*&71yPLv{Ox{7K+cf@ zuYEUySA9QtDed^ZH)zKn=xaylH2h_SIylu3B<0_~QTq@2+V_oLcY}7~-+tAOfA<FM z`1gL*PTX~acH-{7c9D5X?*IW3AIrw16A|Ah6n;x4y|cifHSX-<lE_s%#VBp>#XMhQ z0;t=cATdJisjTmv8;rx(^o>L4k5SnWnKTNU`@Jl<uf1H@i=@Ikk(Wsgdz{a-f+%qd zeu5k<-Sz{Stp)vEV7>9wm+{B_@K5m=qX^v<-;%pV9g^ZZ?}71z=DS1Yi42lwc7{IO zA27w+hNu&t9`A{#9Hv=LQ^DR_M{c-1vsU{|^@_a{5u-s$#}W4sY#;v#1tMKfLzf3f z^RsfE9r(RKo04(<7TF_5w0*lX9_};ftxfyPXDjyJLGS%Aqn0~Zk62On{YWv4zTgN0 z2VjlxQyAHJgsgAjJTNho1=zVw1=|mNmH$il+8Txb!5VSW7L$WGTp>fx$YcmJ*?l;* za~bWOPaqjt?FCkRT~$7W^6E$l)1oa}Sj1?)J!HP3fko_0$h-^{1weBIL#Rkcs?T3r zT>#EPnZy#4YZUfSZtrX&hw?LxktK_kP&u+9q!H3ZW&p<?kS^tPf^^OAZP%s|haoxu ziOk$79jAw=VP`wD83j1R8rie5Yx4o59#KKhAbo)wRM;6rhWvi(eHH8FG7=w4irk=O z1j+gqCq1vqk0pyMo;g&Wmyj3&M6owmikCKC&^!ZCF4qX0t)ZIKRErjmE@*r@0KJ{x zsoau~xe5MRBlpEi*RZXRBno62$`X3-R1SH6e|@J)%jJedM}sq~TtLs=pq;SnSM8){ zueIw#bLshCwL{+DzH$573BUhUJL#!w?H=eoHqw$`wG*DaK|A4@U$v7K+@PJb=s(+8 zgpwPy6Q1~0JL$0-v_mrCUL9ZreimW=4cZCC{oBbjM*8iwc6}Qs{jRTFgqHDh!Z<eM z8t)#8>M*#&8#WnmvEwH9s0@jZ={sN2HvoYb8sSUR`O;4j0ygIco%4@II>%lU8YC8m zn_pX7pk8$Xm-6Zp@>L?2(}2RTj~`R$;R847?xDW9V(p}zMVx(uc1R}N4@OFVvu@Ci zFZ@+I@tzyB6ASvclW~@xaf5dJeSPf+%_aBO-C-mfBSqFl9inq#Ulw)R2*9SGhZd~f z)0let+ICEryMUZR-a|qYUQ42SEqY!Xc~88n?>)n{_mE4NEcx1dqFQ-RRQHaTbEEfA zAX$Dd0#8o7;|A-3<@Z<cy!{64_{sg+$@_hCxR>?i_*)~lpCTWORyEPo+PAMUXubO{ z^HbSj$YU8`i4z|iL%_xF`qq(d%y<ED5cRB6W8^qOQS*&rcBE59UKp-|5|K3FcYX`_ z!(P13-@jim3-cMQP#q-bxkD%LK?oa9USQjFW0A9mS0U2J0oT*%iPBKG&Ja-nZidzR zK6xQ>Mx+en1R>4%$GeM@Qso{k((oI=L>d*&N3<b98Vla)BTRilhBQ(^{a#s0NW<Fu zpe7ExZh^@vgc5NaZcEP`GenLIf2~G%TE0D~Yj*-a82iw7BM7P2-w0+Ee9Er%A*K1R zyVay=;Pd%~kv{*E?~qIi-yt6YDe2mJiuwpB8C=%W_R;r&H)tkU{sUJeQ~!1I1NyI9 z)RSARUXupbMIHZ4sA4}10*gWw(}g^Me;88jCAssty(TlD@y!*OS6JaGzuTPP(Qqvq zF<BO)fDN_!auiCs1}B(X37qay5CU1{`rn)aqdGtRbc*^*AG9pTeHsmJ#B(!9o<AUk z!x)tnhuRK~0$R$GFG=CqkUKZV3Dd#EFO=I=X(`=A<#)+7Ib@I_uUsDr^c{M69t32{ zJv$7d+V9Uph7|vNf#iNdyjYU~9E&U#tH7{ap~{gum4)3Ji|P$D6Q4ORqdIj<BNb>& zO)D^@54#0O3MZK6fqQNn1pW{6QYK}oaTlPH?m7N83QT>ZMl4yEG+bUx1Ry=3zPS=~ zbsG&3iY2Ode^F;r(}u+Js8mCXm*rnUe`LBR{Stav=lDm+{H3Org<nh><{62yf=#FL zOxlBqL|1?;AzO|(wJNu^nj1;vm?_o8(>N$2d*kgt^VhP{@++2^+doXjrY^tYw!uk6 zdg)4<&5ZnYTudrbw%ZbYwgJ3Sfkc_PaOwJ)E7JTgAsI0NA1+IQP~vyvVeSKvPQC!} z03d)60Yla_6!73P<We}^yA%MC-}!;^PZ996UmN7XYvB1&83lXUsK5d{!AtltXDw5- zS_kpl%~4tRxsQa3Hfyneb04ZaFczi~kIfW7evI%~<n=;$W#x}!{mAa6R#&!-#q!*0 zrm|+N+h%sCHJ5(FR<0V06kEg#*A?zS%L4A$+YJ2pXUapG_~RWXui#$6!M>mYm&d&c z$D}q^=JO=Iyaw17Ef)g?$Qz4ATL@bCjRt=~>;;XO*l&^I$r~+FJQZ0a(M=>n^}r>i zj#!sAm6GOM=)>dh)P$i`V8wrVt(!i|$DfpYA-rY3wie;@8?+UU_qNR`V-R9cfEwi5 zqqEq#k6wQYQ0b_(*%h$R$u)cbJk4iO`3ex*Xh3ce^Vd;<YGcRM1~YR0S7(*Jf%>po zXZ5Jcw;RmEweISH+moj$JcZB_0wL%<ZPLdS0>AOYnax$@=iy5*vW`C#+I7UMGh+89 zJee_R>#?`f{I0H3utrQb@;<rB9^TeLn8In5{!XE&FPhdu3?fJrWW>E8RJZ7VqU$M` zMU?Z8mRO?sH*ii-Aqi;j@!q@d_rB_hd+&I9?xUOz3%_omIZlfTYA;_k(zDQho2vDz z2GnH1a=ViAu-28ycVeL%l5dTy@9&ZIWy$mV8<o#0>#IR<<h5DywelUXRgc8%%3PK9 z9!fUT_wnx*VgHRb?4t;Z5S^jhbic<0vtqT=6(n&Xxz!af?VZTFKkY<zE01ZUFf0!0 zJ9y?cKg11!cIs}*nLEI1D)c^}>U<W7VTq_XNqW@3bAWq3$}c+83>CvS4FGaQmj?-L zB|L-l*HBQ0I_+hY`iB#c{*Zp=?1{w3GF#XgN1ypq>ZTKrp@c3ZZIsms@Ahu-jH4i^ zBH3cMPL-9LJ{^kFJufmJ#Cx7LxU0v_sDm&B&D?znI6m<z?{8z_pa<y|;+hj$oEN`H ze5KW0o%t#51%LI}tXmxgT7Q8CxtHQ7aR3F$6m)>lM9dG0)X|G{briVYwTslS_^k#P zY*>vsX0@;ZopHv|Qx{W#L6|3EpyYX4#OnU@grjDYfpW<6oH`i+f#z6$QS6!hs@hl= z$v&uenhTu;x*}Q&QE$((g&)N4vwAfU{o-(0XcByy0phFvPpiZpS^1Nb%5brM8|O<( z@D*sa-$i+mXJL8*n0l`+0kJi1%>d!MV8A@m6?Ozb9W}U42k_qp9r$IH!+{IL{6LYr z%O9_5Y72YP4Vv$8x7rEvUVkJ%JS3k}IDPa#n?sbd8NtZ){V`Ik=)=fQ5pdTJBVSXv z62R0>LCcBUU6gYU7&g@s<$igdoSYxn$av4}{`tBg@8}~3$RP!1>NCkx9o5E_$+^zz ztPRPN+|{GDC*Pt>h<~#e?&OGwD=1w5G6|?fEv{M`J@=kvX_-8y{)f4gD!)SZUBOG? z_0WZW{M^b&#}%@I`xS~LOEEd&q)c}XSG7fte2=SW%~^V?dbj(?Tw#hK%|_?iRJT_( zPGfW4cOm9(<N>Zix*MoDLe4o1deX<6M#g&uS8p%>Xc0>z<L#2id!9n*By^%&MT4vr z%~ew)-*e^d&i%*ST1Ln*z+mo<raZ?r84I&LBR|{qsT{&j@&+K9Qo7l2a{mrQTpvXY zH0NEEAidIEs|C8YX!ux}$MPi_6BiBeBqpHh+!Uroed>b2|DaKLp<6b_L{!PYxh%V6 z_T$6&XF)5j@vFZlQ@*Cbc!J6wLc!mP7F&bn>Cu+WVoMNN+PMTo1=GU6g626<$j03q zG@sCNgDR_2w<6-x#Bszi%rs!DPH_!GHX^tm6c0`gAU@1w2;|0|KZ0PjjK?#eTyd33 zuFFl)R4;euHbz-?!a>wsfE?|ah^iyU-Qz0Ty9WO2qNj)OchwmY{;oA#4ywW+?E|RM zH_E<87=*{K(Zy@*@soXYv5!A4zCk--VZU}V{g@Z(-BnKbU4JYRAELm~*Ar2ycVB@# z;!sR7rVh3mnPU|r7LPu>lpgMnCrHc!pySq(@lq_L%7^}{M`5r5q3T-xgCLjk;eH>| z(Gbk6dx%(rEEsG!!AKv3ojZlraMeon#$$+W+jPv6h~R*``_#s^o+ge0o_V7wAMotS zZ+xRZGj9!ELo^}IP_(&6Myy^$1A_e^)2zN;#$V3As=&GrC{jUW35tL@mD0fcq)M(6 zSiie^K(A@09~^9uE>V7tNM`)Kk$1DmF%cD(Ub&A6?D=USZVc`c-w5L+O5Jr~Jh?+U z-XwMf2qM#a$0fCAr5vh>p-zG+Zecf`Xw!561W<H{nQ~|sXo5^&IDX2tJ@1nP_!<AP z=b{aJK8y_J&>4Z;IJ48qh<7O?M$9L-AulIxFJ$mz4ylm}ZTIP3FbP(9)V@6zC!xQH zqL7~)nf8-M=dds*-ApwNrixyJ>!nJVA9wU&3@T~kj?N{Y^|8X7ILmSzbvW*EbCd#) zV$V<b0hfmI?}txvb6RN2%a&L8pML_2(!xO9NpjCsrp<j-{6~;PCXJq)@l>L_dJOn` z87X-cRgOk;EX$=Xxx~aR%3aB4hZgAwEuYFISJmA^$`f|Rje}`#=;X?^jQK_F0hqY> zcZ12q(zQ!qkP<g}F?SP!eWt>n;=s`>KP~&#gYtoKrLF%iMHp80$A}<Y`rHmHJVB`H zs*#)E6w-0TP&_H6b&a8TU?P`9)l|9Qns6@_>7#hv2at>^W|HT(p4q)W6?Mfh0p(x| zBO{>Ol}xw?E4BQ<(PFhX-B|Sv14i3hY{W#}C&Hw}-NMOOVe?J(YVmzJS%|V1Bp?4! zoWEu{zzO<r<os||E!}=AgVbB75d)*}c;m#0urR~KQ}zh^;Ii12>-1|ksw+1eU;_iQ zIBD7TbtDa0Vxn;1`=$%wtaE$U#ce#c;YD3zlDgrr;qWoAZCUpmTNh12mWAr}rtiX* zE8F7-q<?HOzkV4Qf+#621tK3qX}(6JyL9unPOX8XiwDIOf6S##0ow(Vd%GV$`jOq^ z(O&`|pDqgjEI$D7C;H1<<@+8zsrbZDnhp+l)Gb!HAmD#<s6}UfpCPt!i_@~%;XPx% zH8yM722+gXFMCZ<>1k|wHeKw^ba@-hyEu0O?JgLbRxr-zdg;MC=%(Qa;m!0k&I6LD zFKGEPcnYR;090wRwS;cd%(f?q&vnn8TQ`fHP0Gu>H0v2=R>9H-Cu+EqMXW|m&8Jn| zsM2ENLSf!KRDN(xuiTyIBE?#3N;Zxq<U4^r;e`(loQ9-L+B9qO<zZfUDhzBmwC>~@ z+z1c@G4iQ6NJ8bZz3|}X{Y}ld`90P_eNQxp!?N_$JI_4KvsP2yOz2W?tTXT#)iQZD z1g`BKo#W6|^D3^2h%H>qL)PKrT3aez%`PXzm9`0+*ffg@NJnyDZ<RP&c?L%mZqCbk z@g40Wy5>2;l`tvF8eohaZevHsO@X_ke2mfWx{O=SmxtsM8h~(4^Bd>S+A`|Ru2W0h zGo!dssYnqzGdk^H2rdNBx?RwbUsLoM_!mye*307#*$C@76Ny3(8-+Kt6*cdeqDZ8F z3(fyMj?*D~3Dc$(e%p<;u+kL2BY{g^<tXGL`UV?jn7aFCVGE{M;f3B3#jEH!q@vUT zRDq}mIhB=gL{^;WWmToGsY<6Jv5EM>PxVX-jfzdI>VjI@{Zsbc{bCibE8VV#3kC6} z(17_jKM-L8(I266z~WKD5|R^v7Zn;}r|gQCgTt|=xN}LYTeE|IuA2{>cHgCP=W*O1 z@Bjg%RYh)Bo64uA_0p-c>&SJcDV0;Jsga-)*&J;+q!7E^Hr8Ej5dIaaLfCftxH^q& zkS-qAxr9w0!0uTRg})?jyO>na0sXpeYzxK~p6V9bPeYA}FvJA9ic)*>I1=<-NPDWY zK*CX*Krbp&Lhek5O$9YTz(zaNw8N$jsMP@*G!z<#jSZ+#<P`yCQ4X6n5YGl|s9ndp zcCI5uJ8dZ8rwP~)l%er!N-H<ooHpd$WE?gwppFgLVqkIqujX&3Qs@0#tC4O8=BIye zbhs9+H0zzNvJGa|?RskadU}4fVZM#Zwb4L*a;NGg2i(rdc&gGBj&Ywe+#o=6&3K=> zl#dTLE>gRPjzhJ3&)5bey+)Mn?+BO(Z!C|4TjzfrCo3{eb>{yy&gB1d9BH&X4olV} zuK|-L(|wfXlGco`_`Os8I@yxDUY}1qH;9c-jpzaWcc;#+gVo}4==37Bcj)Nl!B!t! z1Pvsg>hDJPUx$==eg9eiU;Pu-@w)y4|9k)Nw}Ey|I)*rS8Q<X(6b}dBN++Y=RQbUQ zX$2k-KE*~6O<XGYX0={CB>#xt>eUswq-^f}Dm;<i>}%Ts_5{BOYsoKY7x|xmTmJtv zNglGFB%d+Sv)zZzJUIsbw!dX9Glf^;P#*coY=oa8i@^I1AFcNlsP^xfb3j}kx5Ngo zb^<I=!K!(zPj~x=FOw1zcDyJpi$jSC@@7K6H}&LA{zbfrwB^V}F8v&CoX0Mid3#C+ z;+`C?vSV*2bS{l*IF*n-+O}sY+t87aKIGP8$rDy4-{f>XwIO-5+x6=9<jnMQyl-3k zvE(8C>WnkVg96otuH<-G?Gb}el?};I?qqIH25reWKXt;Bce@)i>*f>h;l**aYHwp^ z**rGemZ{9kN$Z^j1AGH5{(;GXsG9?}QT~F&{d;@`1AXqC69*hoH#uxskj`OxZoH}P zcUT&~2ur(_EDdvdzw#}Iht_+LL#lYr9^r{NBpP+ogQa#*k$eP+bE7iaQ+>8Oy)=ph z=OCAX$2PfhX^P(z?Jux8O;OH*G>G3zY<JGtL#B3<AYr6{DJnpoMnle!_nBZsPQy~M zu5Uld-jjMRKlAm$UVIpLjG17Xl<vPR*R^PhX*|HW(lm;I&6ELpw%ud}_1SAm_PQ1x zG$oOYb*2P=HL4KA2C7GOnW6}7w`vg3Z8hT$#)YGxJy|{5_5Q5cjyZV+kFnXqs%DKh z%o}gO--1PI<xBQsRr0w2t*33E;oHV$JCf5T-juCY;kGFrFQ-~FBabAySMZi&?Q*-D z9mx+&yd_($(0Zkv(63#wNu7n4($iwHU8T9+o-|iDXXuKQj+vw<%&$33er!@6hpu}} zo<Bcdj*}_D2mIL{#0OqPtKNBlM?Vh>#Nz!75Q1#wI>)N0&Ch!>s?KL7zLW}%!PT0O zu~1L$oUC0!ZCIl?L}l%Q+Xz~f=%YKI9aki=q~FeGCl@^s_9W1|TN8GB81utg`L#*X z&c4?siSLGG&$`chya{vz`hGG`X(VnBYkmxyAGqpIpiklVr&5*Q`FFy|2pCA9JMiPv z`yxG7lL)QIi#6f$_Q-dPgyJKS7k7rszmt6ih#UgRgh4zE%`g4=9$hjLkYzv`Bj~^1 z;YLQeO-R^f-j~kBR+Tc2ZSC`qw609M3tuuaX-yVk%(lDfrr%Q;NE??)5w#-zov4EH zB%_9Ix`$XKa-LaQs})V`PCLB3ckgc~dgC4f6SwPGyqb?_pb>P_Q<T?LcF;|i|A^cg zWf`OB@*FDQjzNasqNfMd$7bzn_|$QvX$hw)&d3a^A2OasGLElYWdd8vIU5a|R67^v zAUF9+*QqwdHC>?LlUbgw1b3K|d-89kyojJADYKCpHqw`mqnw=T*jrKQgDmN{4N4zw zerIVEIIqgqg!EA{O8$+JL2N4d?IdH~nhvM-*t3g^UXJbSBfwFZQrBA(KAhsW(5KEb zMPF<GyV>ouaVdyASBANICEtkkFT(l(4xejLMlI|2I*a}p{i~4-`eWud2l@{pL-oMG z!I1f1iAonw1Tj1A@ZzGaFaaz&W}4)8J$1%(OS*r2z*W{|8s%_3yV8{5bS>Oqvbw8{ zNU>r*G1yZ*iWK*nG1%)ZD|*s^P;^?_6o!0)Y<IM<DV%9TBC=}ZG3@Ab(3%@FM$`Fk zgag$X*cS7B=Pckn9j=8do$$wWt5p%WIyuV6={LMJdi&Oq$v>?XSY@kfIR~eD$SCg< zHVOH*<b8{5R{nvovW_>dYFfgCv3I+k*q-Lf7_kX+Gw(|FsKI+dvXl!d{cJvhL=)Hc z@z`6FeVh(Ev3=_;#Se}LCZKPi2R8%=VxPwXFZ@l<bZdfYjJyf6h>S`$fs0zLX&MvR zh`(GZ&!W85hJ&UkUv(BK08o4JhsFNEG*B2P@06c0KN^oSq{VIVR5*WcILLJ`PWO+H ziRrMQ8YA~ZO?iRW<78Pl`KSM@QT_|zpt=ukF!n2eQ{g=YaF{)JcCn1)WCcxg5N=VF zBH?nyH0!m}*C@Om)O9Z|S~wUAoSL0Bae25YExc+m64T`DURv6kK(eMr!Kc-7Ub2VF zUk^L04LG)%Tf(>r$gy38P<g6o0o&K*adcU)+qbx=W(=3|&Qb)B07~RQWo1Xe@km&s z3%}#nqnIO3ucjbe{(Lw9wE9CE6bPDigz^xm*~3i>P(W={S!DO%zKwlbiBp)Els+Lb zJtINEgL6TR0qnfxyzpo^{}BKLz~qvEO4w2CtO8a4KPz{}0xbWrrI+ThIH+;3XaO!; z|GlHE4_7c6;tN;4W!(PfSPbhpc-8!yWaq&ib9a(5?8CvLH(PsGEgZqB+jPel7fnow ztlAILk-wqnm1Jc({Jr7w=1`zIi_ocy|7@xwJ6B9FzFwEL!&}pSq^Wd`1M|hkc|JW2 ztDe91ts{rp+b+<x+~!r-<eiHhQLjEkTr+xe&}j+n>I$tr#*obRn-2FUe){bn9!Z}t zF+F2K`jnfL8S6uEiSET0k17FjB|3Y^s0@l@hSYi52);L+4*mDkDPINfadAMSMSt%D zAb{rQ^u-U3-u2Oc-&2z+a=hNST2#Bge_!VhqPJbI{C8dDdqT>Rzq(rf2^K;y!i;4C zBLHw<g!`YBfb#;~|B4ZsRS`5q#(?riL&_P$?GCVUL(o%qR8S50p_2>rul5A_=KlY^ zPzN-O-lQ*ojrh3(_r-DHd|OE7&25X+*=fd{@rLaA#A_bEHByU9kbK*?GJT-4+E}tP zmRKL&A?b)qa2{<G$qDIj$E-}h$LWqK8U7$7XGT>zUp4RXarGMhlK|lY-oT;e$34z= zIGOo~B+>2En0KaiE}=DUxLkHLB;as!Dbkp*BY_?Zqgx>j*`M49=Bla%tdyjcFXe6R z+t*)IdiTf~y#6lt{V8MKHL&wlT4Sd=d`z}=puGW^F5h>#9d$o33m5LMaVO0}<hI)7 zT51k*Lg{5<vKJpm$vO|Cm!7=rW(MEZDBKQpRxQI%X4=#qCf3Wu1Q_muZbzf|?{6w5 zBBiNG>t>_)$B;F$P18c^fQ%34-v)x5(a_XjuISAFJPbjg^^1n0mY>Oj&oSm#;@t@x ziX;UJX>ooSZwvGAHkh^+9TB-4j;s-Do_C;v?(=P0aWvFgbqm?7U6Kb8%TPqz&eUoV zZ3Qa%h%Zki*^rY;m@VhoY(eVp3v^H;{7B{R3k%O)hEEA6K71alxu83HRQxUK)ri^5 z&eK>9J}zN>!YIbW&|W6i$He%VxXYKiA>T(K9auv2YU|E%JC0(p-8KZMx5|r^pi+oO zJVXl(VMQDscCuvg717Ph+>9Q1y2az)K)s^O%$Go8n$JL|gJn+WX?iP6R;VUxfQ}i{ zl)gnOYi+hWr{c~48`b<O>EhAL!sp-MHSS?$qda+tV6WLeu|^o*lmFUvtK?5nO|OQ5 zDota1Q3Y85A+P5z4R?HA*l`b3f3>7w0=ReUPcRdX80w{8;HNNU2&jzGkjfUT#x>u{ z&Eb1G&5wQMp&cz6KSZ6hkw^D4t{qH!+lhaRPyW<Y9IpC_QM#$w=GM8SM`S$V^Up6L zcp^+Ec(R~O8i;B?fUgB;&>v6A_&OPMe1`_0f)`pngHbR9!4)#b9WjC-tQkyQKZ@|6 zvU`)s?!Bp7T_xQ`stuW$ooj0u0Uvc3RWvd|%;DPpZLHg*awJ5pHo6m{yT>8ZW!2@i zPKMiL<KI{I?~KM{?%ThZaAN~=WW0~nR@~vo4!nXL;PWr@_1{1UCY#4S0`C3N6YMiT z8ajS7TzZ@Z!r_a*kQS19%2O2`p?-D`&)uDt5H(-T%+HIOpQoLbmI@BqlZX6C&{ZL+ zKkdweDoE|fQ_okk^YfUr!l;yv`Ot9YQS;TR`FZSYH7a4M3Q8E{5KYUY(OE#v1oG6g z(%=lh`txAE*81{b+|_vVfPsvskafqi?m~?dwMz1!T8eSx#iAc1eg|Drk=vy1NeH$} z>;pJ0|7xteaai3VqJe)Umb-6>ZS2u`L?5=~9%g&`y(KlV;G2%ULpMD{mDJMt&r(i^ zNqZWK-Zpl?L$nY&Or~gyf#7uu<Fi_Qc7rfHT(m5X+^gR=6#baSC3&m^T3G&KxM>a1 zybv0A8=-;aM7t*r49~Zw=4)g{A9Rqc-&GcXw0)~1PxYd4fr@XXNKO;~w#KX|w{^hU zr&zBJDeRPje2qiYo`PtPYo1%D_7=qYY^j?HHrYJ|aiwbv&TS3O0%Pf)*5Pl-LM=C{ z722Ny<MRvI4kX?zFrJQ42O14Hh5>s539>7s70l<pR&f9OfvOAIR;bVq*T(MNxz24* z1XY$oeruxnYs0DCKi4u|YeJjGYf*Xg^ewd19)hEeFAs%eq1z~7T8|r=_M+_StJ~Ar zcfOC(L{Wa%6KB>Zp^>W``Md)d8F7(LdjfKrL(#U~91D^y{#{dkAnd8m+`GQRZ%u`W zb8!7sv~*YpAWYE_rCJ?}i5|PK$#0KutxO;}xp%U}<0N1m;I<+ZFMdfLoKn1ba64Uc zH*(_bl%r?(d!f5R2Hz3}?Z}ej=+ZVrcPvt_Alu8z^{)}K(?=h-P)=(q@?Ay4B05+; z4s9CgWC-s~xS*|7i&2cGDO~jg>#8-lk(=&g+`yhjgg(MoD`3$C3JpQV&l&uA!?@Vv zs^i9eR>+84jO0;Y(3$6?BO@<N>(KB!tO-jB4UT~-N1mSey{maO(*%K8C!>qd9InD` zJIFbGV&{_Q5#|NMTadvx5Q$WWBb{<$t-3{5v@o%jIX#aJsK)wYC)Tqa$51I|qI5!C z_cRM77iV#PJVd!U;C|x0JJy+}bLQ#OZ~znNHO2GoMqDP)TdpaV9n|U>EUH!`oKZIo zm64L;f{YL2#-3H3H6FK;iSX~JgSvp-RJ6=UAm?CNM;<NxTMhmI#zSnD4<kk)2^|H5 zz(AlAjyxU7dtHbcMPuFii4EIVxMxfRNU@9Q!(-My4J~EmR)eB6#-x?`)-%2HTT@)C z;lGBz@Gy_n=ye}DasZim@50F)A^-nR{69&|ja_0Jg(meH;gLYSZqE`O8K5<xcoeA& zGTmc0OS3OJt>%h}>BCjayK(OHXN$g$uL6w5yOWmlRWY(nP|S}JH^so+WX}>h-;Mn8 z-0j#ehIgRi;jnrp8wXP({4JC}C``IPm0<pQP%9K5=d^?fw8cuBLYh0_M1>I1BWk5Z z`e?%?tZ;A#$>AasjZ+C3i(*N8uhrBRE3OZLaq0V4*b-U`g;9-bn*)LP(W>-8Z2E0< z@o-Z;i_2Agf%aKVK@GBxiHoTvc_e|~H|l(ASQ%c}7|I_UUXnM2?2NcyeR2-+;h$@v z{p7Uz2dn&fdVY6ElMoJA6R`5^kB1P&h{utAH<g{I%a->GrF7gm`)dM2w1Gk-9}}C@ z0EK1}<xQY7o+RQClyO{&_<X2IPx){L>vb<`uu=lAJgOBU<={9>6$BA>i{FQksJ!<T zetD?sD1!4^=(4BTKmwqOWW?M+5@AwE^G}A#O}$M=2``46)&%z;t-BC_at?}i7V4aZ zdLf_!LSdS1Oykq62WGWG&RrOTUK<X;^%L3GTWC)qGIH`L0EaZgu^KL%v8af^h8x9~ zhVqYvmRJp0_xf#jV!BOLO2no@cTo{hA-W*I;tQ0fDm0(ff}ntsI1*@C!t9K!WRz5b zu4Is7jb)+o#!&ByrA57KY-6NHL*;)6IR``mO)?f3GZsy20UK<<TvCMBLhzaf7LZ@E z<X=`0EV%aXs!-K4EH{$io-ij={xYgeDwrrf6e^z^?(0c<J=F9w(S7;1oeOMhM}{VN z584bX(;vgvk{z!^`0g3Fb4rK|wjMHA%n~c9PXe8tup6~6J&6<{Ny7hxuqhoY81q05 zxY1a`D!9?`FN3#`mazhv`SMizp;qx)2k=|2YSO7Q>E@ErbknU+oEdSqX>d=DST<>w zCY9<+f3jrE(;Z-L-_+J>J>b<j{>~m$`{pKMcrI2+3VBuwzMt0wYH4YEh`<@c?_deL z_xCR0yK{t#<{#CN|3B&y_jR?<b+KwUK^=J0aw9>k03~5+s9a5Xo7S6WUdA=TKdiw3 zhqR+Om@6@G7NRI_=_y)xnQP9!5>~iOUm``=Oz{rN0S;%XlZ`4m+=Ee&Z-^iB4bsG6 zSGK1|l|Yl1PHEclZ|3&(?lbG-BP$Wc^_bhQB=6m~SOZUGWe|RBkGZCM<iW#>v6#&v zd6{1>R+b4H{c*BVq3aT0ylQdBw<MZ%wcQ-jaQ7D=L|xg0wIzr}t^BjW3AXt6A<!DY zIl}BBi?K)`FTQp){}3)o!)~x4S3CMiHsYmA<e+9!WEZ`Rd3kilzZn_SXSGG-?vl2s z%lCyzN)IkJ2vh$mI}thc9-Cnk()+HKuS;R7dZ*UBCf<kpZV^w2<6H#`u<@8ogf`UE z{PL@~i;z3KTm<L+fn7$Rg#>{nT*OfPdUbwR_~;XC>o<hQheOoqC)iUL347Rt0@5<S z^kSLMI^cMLus-xb8K${K$3@}w)!GRcedxfcF8?1{kF!1+Y38u0M2|<3^NVxBZbnBm zuQd(k>2cRCM<AGTITD+xbUW_G!&-D`J{jAA^Y^q~p^(z#kVc`lpuBg7I+y6NR9qA3 zDe<s%`k|2~<`^w>E`d0v;@!T}A<iY}IpOi?*TdvcK_5t@g;(?U%VG%sJ9X95tP`x_ zCPU|vR4$p=PYdacC$f4;p*S>@|1PQevO=9-8wPuJI2C6s<H<ov1IZdH-%ezEh18<r zO?BiyDI=t*!gu9fHuS%j#Vjm2-y3$S3>g>G&JQA2fG|f55Mcc<shi&=;m+t0w^TAH zQ@CPU=MuW>=`Tn_C(9B>9MK_pI{ra57O8A|@9G%7nDpeWHlVYE>k}X}n-^*NsE{xr z)HIOFc)A}wyXAU%c6$i0o$%CT95S)2OMFK@+F~j>+G1&!^io%Z+^vmO1I}#>@dd&u zKmk4O8nNRigcOnpWq=khNKv5R{9~aebp$+E+JI2gZ%DnFHe!-sTQs~aRP;KF>JXf5 zKePM@k|WP{y-yM{sb&>9NNW4pv>S)3x<-`ly^5z*gW1aW81tvv#wS?#nRsW@PE%~< zm$s8F4=zaZK(e6qNJ@fyUQ$g*J`O)4l*kqr<&n4(9^pq{WxTF%0QRuhj*jmFvuQ{v zS$~23A-QIMy+f~qQsLl0T|loY{w<UIqx{f(1vQI=0Z0Yu$<53^7p@WANw1<^#)8|0 z7rKb@L7I0CLM5%|g)W%f#PRA!pzstXz~vftHZL6$prLxG)kvRyZ9W2*5DC>HkA_9$ zD~F)ZKXUG6@~&X-0=#ZgLmH(e(gS+p$EZCt?WJRVbc~;ldq@R49fW+7EacZ}(QUU4 z0{PLe&2uioHH+0iw=MgAs&fmddKJG$rLb$({Y+N-nl!MJYtrgz>8`64bF$i{oU6@q zCM|>6bh#?)L_IC!lAlkAlks!bLcINw%5nwN#D<?OSNJ>eGvZTK(u=AzqnxAcGzEP> z(LBL~icC5-L$>Rwok#sMn6C5*C1YsKO>hby1py7Fn-ZvL$51j$KQh~D!SS<jm8TL8 zt1}i5iy-07cq*icMvsdoq>n2ZiZZiQ`uC&Bm2sKH8|CmaBbPDSF(3@Y7xK=~j-$df z^&vI6OuEv4Q!=JC+qI}4(G@md8r#%>my0Ipxo9%cB=?NlH>u%BrQuB>OoGtl0jf$6 z<e7K%aS7iB^Enkv5k&XXM$!rUo7NZI+l7l$3is<>eCrbFNQ?=!X(z0YK8o%w-g|CO zKK8;<3`x#oj0WcsOY9?Wmxzf+A$4L`P+kSR&BzgN(fZ=$TMv1haS24NRyQ3cZrB=V z_0)nk<YQp%)_b+l9&HpVB%tQ$kpo_>)~(eLAN`vBH9PG-ZH!mTmA1j-3)OplTD?ar zXLC4k<iNhgK5d*=8;hi}$R}H1NJiX?(V;bzZZd9O<**wZ1xBCN<g*P6XcPR}c%-A1 z>)ZP6Mt?#4>8IG{xo7R17Os<y0a2v^CgXAX%w~RP-M%UxB#rw*?u01MI}lVH(4PoN z9C%>7bExivs)I>~xWr;Tb9OOY+^@a+c8dJw$yfWoD4DrKRe1Gal8Q?VSPgz=a7RZ% z*Wd)am+`pTa(t88ry9m3x{dK}yAfB+-jp$3JUTzX$;36sG_NjBQB~YlJSw_*0hg%J zQuvh{jASB}muwxuhK^U(0rU=5Q(6Xpm0@d#sC`zWht*bYMS-B$T6$8C*o;!@@vhy4 z&2Ml)SCj477R&>hAK?KtG;XX`DuiT(;tCF<VRhrNfg_Ra?@MUnpjeE%QgfpDNR4;f z83KZBKcJp~jzh~I8c~?N>mzjIFd7|&v50XwHaN;(XmH}org228bf0?XY4pwCr$%=_ zoQ4Zko6gOu+nZ0+T;CzfxeE+H*h(LharO?!wuXQ)K43@rTLY<RI|d@F5!_kPMiz&v z(#=>bht=k!I0e;&BPewP?!6^O_$Ukk`H;HEf>97`oz72H)zXUiYCz?nnB%el`@vCf zBSdDQ5j1)y6o1l1S~Cb8M1J>!F&V-|IaR})dBaw#x{YV8qzmyQnl$Erpo^Cwkq-D= zcV1k@%^;e0YYwSTHv3ctb4oN4mXmw@B8*#6@;yG${4Z_M2CaM{o~5zGLuxG2yv2Z( z6sJTO1L31<Cx`zB>WI}N@g~m83rB;R7Ok{7T&tC$)J<a}l9tpM#A#*@9gQ3+X=4~2 zPdKXu!O*BR8R4=g*wWQRE3FwOGX;?5^M(kgX*bvc@FNSaUB>Q6?h)l`-WiMtNo|a@ zIaq$GPqg57TzkTA3TjS-JOiSzf{Z5;2z8mH?YoV|kAg(HSkUh_t|z7F`lJid8|;%V z#J7U^2SM%zb)7Ynodsa-P_9le3S=A$6;Q@`I<+`udfy<z2SMZ!yY_E=Fuy%qoN{eU ze$RDd3J0!@DZCjh|2ow7uDCA<_tReHMBH?3o#L8c{%6F-V220k!j<dC%+}I=MtAgc z2Ed*V$E^7dxC)*4!WUC&KCboKsM$7{>WyF$RN~2C(-4Z7Aj}*w<C~EbTO?&Eu>rHS zDp!N->_8;L81XN?TMtdmZd5ymUKLtc$0zNU$t}>yy?@H@DE-%p_BOver@c-5oKzwF z0h(16H&J2l`H7dHr6Z%mp?(yyGjm>>szoJy7HnaAdz0Ti@r860ZwudD_x3G#8*8jW zdfC9XbACmNBbVJuXbXqIUXVp(xlFozWaPHXFCe!aT-YJeJyb_zy49w}Me4=4f?*kd z{|<JxH37DQ<LnyZ-vNsP#A8oMmDmQf_6(4vJ@MTjOb4|Lj0dA*fb42%FOlk?TqpHX zJVduH)n5UJ@z3DV64nbI8BSq^^dVNc2?ycwMe#guwecV>Ux6ywMQesrlXk&}=M2=P z!>E$KlD}ux=k-cVk6uMW@qTxklzs`l9}0p2Jk1eRiFaGOq8~Z9-YR_4DKh7F%>l#l z3R|x26S3x8EvmW7HN&A-XzLVcMLt|E%Zn#F#bcc}WFm#)Am9OaUI#?P9lwzK{zU>I zO>_7lWV|doe*)Fi9d=Y_u3Ue5XIa;J<atu$)%&lSb=}F&L%NK}cv8Vtb9>iFsN^21 z6F&^zTE|U9M^0^|g6JleNV9`3LAugI&wRRs#kvl$a9-tz>XM5&tlDYaXZ)ayF!17H zdM9hX5{>Nvy;<DAIN_AzbT|@FVkP78c(*!AYzV?~q;1jdV5L8h!*p%;*7fclAFu4* z+jK1gPRR}u(+}^yQvOyrqJ{ov$Ii0OCB^{sH&5#^UHS57HE;IkG^ChIid05qvbAut z+SSf+1ZqDM;-!}UNMl~Gv}ZgnI|#KfDA%V^{BrR9(I8}GKl=Z1rW3J7>+``@Xj@q6 z)zCwAMBXH{8}NWZO+|`qfy|dlcKX&j4A7zj5cVf3y%R0V<0di@6gzC1iX7Iizoxni zX#f}jt8OhLyc={E=<r4QYtYwdB693QbsqGLy9*Oj<i8Kfz%4(F?#mh&vO6+Zce0yT zt*$Z?l&@Fws|guC#JaGch0*eOj|L%M%HJOA1+5T7X>2qur*~8-XMlSc2LlHIf^B{8 zfmkcNaizRQhLX4fEn&maGXA@?{=1?iCk+(eOi+9+N5q;ywLy_<y4J@Db)CW&?OTX~ zV-|7g7It?EpSQ~jkNaeVez#MsAqagmR_#roxsfEqmBA5gp@rLm8e|+NV0x%`vSL7< ztnd<zsrHF)cyWJ?GGSS%*E?%jk#lIR^y-zSm%@JBnvZI6Yl5a$h8gp}G-TPtS2vFq zo}>FZ>yrhA*J(d4*pC@=4||kTV|lG#6U8Q9-N_%8mmuYmCGguig+KM4$+B1?KoXsw ztnM2X<TyA&kRh=N3nEy>X9vq4BsYP&h2d`tmKTPr7PI`FZ2oM>9FPU7Gr57X7(h5m zVqG2$3WO6}J{tZ!S)e?cIiNfftB|-G-!o)G%WAL{Qdto1Y95d?e1oXUUNO#X?IpwM zrnwYISkuddgq6QS;Q+gPkMqt(ZlZ7!KJSWoC*}BQ;SE%HyXolD5rPQbK0j8)jYh7> z07GGKGXAXF*a#^a`47#9W`Rd{Lkc+uG=nAC&6st$q%X^PaY9hD0C&@<QdmPC9|#~E zJj+(rGUkP2h`6wts`N1CzYHghQ8Yb9v<1uU5L=$@dWSBbN0q!w=f94+vVJXPU&scu zs#=ZAPWuZCKCRlTg?U5f!HwPm6TJ71as-|n=W9$Q=f#2361@p!ose92IK2q9LvF(r z<$6|_5}bz!DN+qmx$(S4W8}u;r}knQ-%PNK<sYE)?|_DHET&RE4nRGJg_sXzcCF9q zN8!ao9Uc<<-sk4j1TCFx`!32tt-vOdHUhiSwI0q%qgqci6phkBiFsx{!k3Mus6ljV zG<Imi`uKhqt?+RN`a?7r`Z>5hx!)71@jnLhR%aeu&l2fy^9;FLmc$Eho?NT8Jb_dd zkHP?Dxg#2Ul6%`Z*E<d7b_*^BA(04z5edqYB@PWLqMy7IU|uCuXXFs>EHfkj#J)MS zs@Yf|V*ekQHS6btXHOkF+qUNuTJsuZIs4!~Z7nPPHfToHC`SL_=D0fO^>;OV>>#<5 zRpzfqq7Ds)15b>q3PCV60Y<05K42Ri(9-Q~0oxcy0uAm))<47J0j!O;akO-AF#kTP z64Y{PXYbU6A;J7F!_KC%qK6QkRId?61gjRp4uK1PcYhM>P1JY`4K3PUQVaqYO>--W z7l1j*QVSC`cyKs&?*5r%P9o$6)h-Vmh5pWmGcTL$%c16qpr(`(Y>NDcb?_-cLKu3O zqE|1RdXp&{cmWF+wq1n#vsQ&0ZwgJa-VwbR2@l{nYymVm(dF+%RDwQhits?NXgahy z9xWv-r~R(*nWQfQjNSzi<lV7l0N_qIu0o=WQki#*!C>UP{DV4$!!=@j@1(@oV18Tw z+l_Y-w9z~UJ?<zOfa1iULRLXk$!RVm)O^np<aPJk4Uh~m?T#Kor^F2xQT6a)qK21F z_iV*6YVv_T5#t7bv4?Q#Gg@dEu&_%+XJz1qD+HeZrl(eeQzaRJFj!{(Ct;-buv$>c zg|>_k4Z*E+({IBYPrI!K<k+f6bv36}9$MFQAw<>yH@2%iO>q1mIz(=b0<Bs`Od({q zv6LD6p6e8m&dJ(&D&r}brin>L__~M4P&l99Kl6r&Nu>ERKc=2c^24Yn+kx6Nydh{g z-}FYfkE-KYA7Pg=WO&N9o(3;to~w^&EK<ZTd*Di_j3a(S7Jm9^f)LycpoHIQXaQ=F zE1-=L|J7685Iz-P%uioX0406a)AW8gf{gqsq&RU@8vv6{mq0b#g$DNn(M3}^Y&gu> zxZ3$~L69p@DlaHi4DNN@__Z`KrXIEuDrQ1j$rjxYmk!5bdFEXSr{Ktl(caY%&|#CB z22s7U=0EQt`_slKI|N{3i;%2tEv3c^A}Ypz^pvj;R{^vBvim&;9Dm?&b_9H14DOjc zoexG+@HJ1~qev&%^+P?xPKBYw(>=ZJH_Nz<mvEiUB<d9KJwgXoKv+C3#*a0`b@vId zf)7*AZ;i-0D0X_J#{p|8OiXzsE-o_fQQ~2F-ii{r4JDEdO4UY~JB~8KzMlMrVbBc^ z?v||Gtx*5$#&vPveHnGKq@z6=U+5HQ!wXI17b0_uZNLn2*JdnuyKjsGJ*3J9X@M(7 zINeijAmzQ*C>$3g@=g^7=v-ojrHvca@F}7~2st2Vu`9Z_6Mk<`6A!dlb($N4a|A?a z{mM{!ah-w;sDg?u%J^|0RMIDhlQUYQ?cEyjy`H8N3g@hmQ-j%V_$TRn@A^1tNT-x1 zvqj=Z4A>%RaHn*a%u|T<%%xa0IwXfvwkGo_ox;Fg{sISddr$sWtU<v#;cvaivyBnn z>1kTu`y>TJM-DEqwYn$2Qr2g>U18AYUF);nnfE)#!Y8&5$Z(ixzeYzW??w%TyR<&h zvW*oxd%!M6n!<jwrGym1(0MfQ7k$6=GY5pd6Q8%Y@jvyrHG0w1Dbq1pj3Gry7}V#$ zg{|tiJWd~0J_C1ankRE}gukI`gglZWMz*NhSPs!pD(PZR6AJ4<MkH|uoZ2>+<(W=M zdJyuG)7FrB7KlFBK9EuBS}r+lMRwz7S&o^Hl9Q@Vhw0UYY2;}hN+v$&JU{WZcW#w+ z*2G<-U!PLwm;bI$q34(X?6Y&2x+1S)DDn80dzI%}F+NSLP3fq}PBTG&dhPe(cr)t0 z;skY@+he&S;@90~)Xg1aUL8|B)XYIoEPc~$-W<c7kWO}+lVb4XtiJeJGf@=_Xgi&= zToJzNMlmlunWoEneC0Yb(JqT<eL@HI^6_r-93AN|YSN1v@T?s@p1>H<&`2M{n5d*- zKCCI;X3iqYXS7a=%DTdTfiV$cZ$@$Q6WiAfAkPG}uF+&&5kBiSk3e0V4;N&8x>tE5 zeU6E+Syv<<CW5H2E&C>&Jh(1i?(wtkElKF`Xm>>oH>hGbS-8bZ(s>}~Fc*!x0HO3p z@TUp&R-(&M$}c)!4zE8&zI{&~oC8iGXK8k3`Vt=NGo@l7rsQ<D1yeI6ViG13hBL)f zEoJ!%h72G>K3mz(=Ipd>ji|F{V$!x@+~PER{sW&&@OcQIZhW@lm48og;@c_IOnPhz z`BsW=hgRa7yCJ0mBa+{8@C{=s;Urb~oy4MeQ429Qo=P2NNV}gR1F_aA4EUnrG}c<e zlwS@PFJY}uu=$<gVz=J<d)9%rnFh_zVea?_Cu==N+GZ`>?$pghg-t4TWNJPle47tC z*|aK>O=i-|$DH~!SE(P*MyCQIlfLOPsSwWMrt(SZ2?86i2p<nbVrKH0BzDT53B2-Y zMxJFHoVzk*y^4+M2_Pz6EnxT&kY#^DO>V?1<KqaJAAFp^e*w$~|KS5aN+kXmFW>{u zqz(kp6q{}%FY_JB%NOOB1$>YOKgutogopg{1^HzWA9%)JATOt7lOYA9Jt4iHlixy~ zWqC+^;774tev8Kk-V)D}x0Z~QoByE95aG@X(H}lY^D|0we2@uzPnx@jk;(Ai%k4x- zZpTZ?WWJNz3E!a|_vK1w3h7GtR++C~X@U>(x*tul7Hn{u$uRtB`I&S|dHoc5c4WIV zPKM4m$_wA%$4M{kbw<gre~tfS9GTRanp4=EFhL_zn$#jhYG(ZLr?KU>jExs$N<i?) zlZ$+Ij?ULo<dQVSCY=NSFX?<CMsqdH6&I&z;kUnM!FYs4Eg8+3)7^T4{+g^O_Bu7( zEu%E$mk8FFej)gx;WVQ(K%+@tpK<=PyeB+L1DyoMFwc*(J;#2KR`nD+C7Is-w)O+l zlw{5JWVHpP3a3@gZ|a6k>WZ+wySWBtFXuK$Z>f&i(V#YJVFR97u842pncEcY+15p3 zJZDE|0aD^0$$KXxC!Tpt(Y|dt<c7qvc5KKx@~oX-i)VgQbYNR4xv^$P%NyjxGwy`& z2RvI_!eB@+B^ZzFwNMFq!*EB0H$iV0#Kcv)3eo$vhK7BHh64@ew|{of2INrnnKVwY z##>n=Qw_}4e2}(bBXU%^a}9?~#d_7Wv|>&*4W+#hsW^r7BAYp*NsTe+C|%3Vo&>Ln z-7?8TtKIM9xOO>&*L8JIu_Tn~FFeR`=0%^0b$!xsY!{vHq`3WJ#>D`BkQ%!FN^Aay zLW}f^PLsy<dc%etbkl0I5crE8{2(^$g=gdI6u`<zD+ma#NTuC9)19=|d-!Mb$sxYO zKbc=1;y?VEdGL_H;g8LKAMB)~9C(yDm^^ApA&;I)A&=rxumOzlDtZC6U*3IE=b*KY zL*3>VlJSo7aJTtv61IU6UMAz55SF2Zo7TFIoG{NB<e{THpPn#(IuM(|@Gp@T?feU9 z;l)gv51S_?`sgTM%VBdZxo#N#MYP~fAd32=&QELo{|uQ24+zjvfq#X}e@MW_Fw%3R z#S!V*?&fyPL%+%#6_07?H<?$P%qZl2L>O?<6Cb5OKyx<%Ny0@RevqNqlmnpg@fQQ} z(FzpHC!qyzy6D6YG87TgMSvzeL#BE~T-1GXqBmaWj@Ns(QE+wH4a~Y<=e}Q0V8TCz zMzEGXfkuINoiASR-_`{-{1n{A@7MY6*AuWvPmrEYNROkDJ6`9A*E_f60I@5Ph3<Zx z1C0O~aRF)6E*7JaFJ9+~*L$~>!i%Z_y!`z-&;2-V7_J13fUl3CQCSJ&k4LRmUFnao z3X$RbXhh)i7-@7wvZK-3DNG<<?}*n`h6%szxL+4QBOoI=af+T>Rrfq=$wGO8wfsSS zt(KpmTBLKJ7{H*6AMSorm72DDP1^9X5ku3EiRuZMVPC{$zH9%U`NK2IWDckAiS)d# zXmcogTLZ+%sx8@j+>fBL(%lp|ba=GYM$e6If^V&+UMp=0Z(O^h%8{sZPs^Mz(B{6I za_3QVr){qL05d~*50crSUMp@6e~D&ZG`lBrf+b057J$lhu>U>^<rsHX?V0~Mw}9Ny zK6x#$=QO6)y@18cwoh+U`|P<KJKJWMZ8M7dpaRaRdl8SM5A@=KaNRO^9lPy!x*O+7 zd+}^(-Ai!Y=lAMo+g!Db)1AW&%$7aU9k!}6_~zMf-@Izgc+i>m!YA9|LTf%|MZE-= zJBp@R-PW1r_Ee8*o#)d~(Zi{erV|fWvVs=1)EF+h5^6*O8V-hAQt192OSI3LTX6?h zgY=Nu_K9is+bR-gPZ`3kv(979d2q#x4iw}E3Z?`K@&W~S!{8{5Qsvt3ciZ#530g<h z(luppR%&QS@Y<Z71eIs@Aa`Pvd$z@yh=S9}ML#^4X1(7@8`1~S<(J{x=Cj{hK3C<i zP7iErF#j;kH&EpqNV^kh^q8^GJc?=k+*T>sKo3dnk%D}G!IXTO8pSvZrd)`||2|B@ zI^ADzw}<#Q=emb5?t;7A+QH5^ozqt4h|@c4jzAn2u&oe=sxU>4@oEPXmnOe`n$JE{ znI;#HqE)xT<Yu4hu-_A~-&^5#o3ySS3C#<+kskZ48u$`fy!Kmt)_nN>`0SHUws7qp z>qOz!?&hSXuV}7axEbH_j{_x}2bKQ-2g=FCgHyOGXv5!vCz{V_?g~F1-^!0FZN8xc zXfr0o0vFzi5_yu}Oj~r)SbS?bthD(8zRZ)0Z%wgGL>qA&dHWx<WuiD5-;l>p9;=1^ z(1~O&0BGgSD?W6apC90?TDo~HDgv1gCje`Nfhvzii~No*+y}Sc=RI<`=))q*D8#5* zlFDBWoAd7>Z)-G2<mIzY=Qf*zcL%gFK4X5sW+2{~AI;*P;I?>d4wNR!Bni@7&+Qn= z#pJxU8-1<`pCGk|_HNSoY(^lx&DDfpCsHHq;MVV&@010IZ2Ro;J1D1Z#XZrmaXU;J zqU;=~H@^iH|571VB|MI8xe4FJ7?oI}g4Nk;y~(F?OmD8Kz0Tq+rRZ>v!PWN69c->` z1+;Ft{|G@sDJr~*x20&6^gHAf!B;*S=y;zrpY)ZhTH&?c2NO2w@IOr0(!kyh>$!@z zke-tpQ+nSzbe*%-#_5oE&pyp<zo&GQs&eBvr~O_m#Ak};mLKCX@s$g=esAmgQ%c4n z9r^EZhs_9mCBC-SG2Ak4_3q8<oPbG%*FGbkrJ56({zBuF%V)ieyzr*aKErRlGut}5 z;!bWjP@3n%+?cJb>(A>*IW1$p$2diNDr{c(F<IPIN?w_8mBKW)>QHyceA$G*`uEK* zkxz31`RhFrGFKSM&nL_=<kMVZz~A(xuzwr<*4sK?(WAmw99Uf$$?HlOw{eow`u`Yv z_xL8N^nZM2GL!4{l2QUR0h-YC8nhI&6hWJ`q)@=6MT)ZM(qbxCR}t^*+6J~1C|ZIF zh2kcGaH&+=sFYjXG_r+4>!(ClE`p0$qkt`9L2lCK_db&paX+8e_x1au)6C47bDr~@ z=RD^*&v`DBiI7q-zWrj=QjpR0EXLdg?VGoF8d|noqb%cs&f*8~+jW(4Wx?shS^S_A zw4WF(nk~Z*xAs1lK3KF_iXw<oqrXo1X#3f}_-RM&eK>(?QCgr#)82<42(Dddbo6Ma zSZSrTICs?}IX>-!ZtXN=sZtzt7iYi{q&U;B%>`v4?{K8dUz}5Q8HTKjEE>7$cHZ?d zjCmMs(;}zR6!mTrA+b1j<G#;H8_NR5AYoF;z_GX?*dC*5Y80MZvV+1?2hLoYS$FY$ zr3pO0m|p^V#mIo&m*RZuulyT5Mx`J34As7pujE(Ti~w*7KLz+9n0S-~V=Wlpx<3f6 zq`Dq4l7+So2>A)C1^(-QqWogkCk9V3mrfY%6if9FfvE3yiY2abv}xN9AVk0RelNX3 zAjFU@MaVKEzs_<+<}8?oqVASAzCMVs(D)obe?4f)#82{c%Rv`hQ-!Ao59-NJHgJma z4q}`}8}uth<4aVMr+tzH(0Wn*Jk_#!U}upS=E3}An{k7+cxq4aG(Vx=NxZ1Fh6ejZ zuH)iIF`)+#OEECRVIgMdEuQ2qo+kG6jbv3#19jRtDjVX`>c6BCGbSW19hYd$%#0b6 zXd9L2ybrsAn3P0!q9zL-)tV_yF&fRBBQb+Ce>oWwqv`u0Cc^LsL`|?tlCO_ta59G_ zHHi!aA1gLf%kOCZt=c=pr=3&Oq2jS}74nfiVg?Kc48(^GfXLbspM9-iO>i9HE+=ae zpxX1bD7(SNmN{GAa39k;kLmU!KMg!K*Z`Q=YfrZ3_wlvDVt-`A(Tb(CD(OSQ*CbID z`}=$nc)M63FZd-9Nf*q1W3<rd?YC_**~yw=CZiIsAr7N)qZKm%bFyeMv?1UG6}~=x zi@?U3-u@hi)tv~!RXL2GQgNUU$~t2-ESPR@MKwo49FWvu{)K!(cwp03zqqH*dh87U zIlrXrn-z!)SvthRCx|a1Kpp~X$(_YhZCH^MU^MW@#L6^C?%U=$Su3t*T2;8mNEI44 zU|Kax5Mm)yfn`nKghX6s_aKFTki17StjB2c7nt1zdC0q-7F5+akt4f!Y6UEi+xpP? zqJ_Kor}sX=AP5tmYd#)ytE5Et_qx>I@m<j{X%DRw(kp`a_rArLAEm*bFdD5V`cykV z5qLe2W-1;DomSDlj|8OIv~z65Ms%ADQZ^=P_LmK}d)J&B_YWkJYpR9FAsx34%m2nr zDx215Yx}Nf_hj`)ZllsBiLe=?D}ofiNf@S88;+Yr^o^|<XwXN$)<fI)VI;HKWryc- zI;2zJ&u4s1^<+LT%6t+|P;^LFmGiT?4ww;W2Gr6I1v&;m&^H20sXD?c7LEHKFHd=} z)=YZ%?CbsaM>a*+RGa`IS%x-^7i!(P;W%00H~VRO(Wl#m;Vw<sCa&)5tY+P`l~1nT zw?X`HALP|1F8j1&-P&AlOZKsY9dfrq<}RM-v@iG6c{&eT-NmD@p=tf9590i+%YH2a zcrCY|^ZbC}JkjSre5>e#Y^!#n=BHKvU@AJ`vGAx<JFOWt<n7$enA`fihi|FIC-@Fu zQ=J>(KYUg7*AYF3&!~ovupa(WwNGa|+@^X$=REwG>bBOc&D)`7#LFq4Hm66M=htSc z5%<`NoWF;3zT)XfyXY@YT~*IiynyJAVsIb*KOoah_iA(8+Ejb>HRrYqNQ&vyrW`wX zpl;`Gsau=URL_X#Q$1Sn-#panrK7#Y**@buRO>gE9y@o^z=-EkHf^>QONOx|=|Y^7 zcAU+xO>~PROVc8%cKWs1UP;9Hc6h4Bg5B$X1fh<{Da0lGpKyq<Kqo!VrycL5Oa01e zFbC95@D~^S*OKh|JK!uJMrzGW(YmOrm#xZtYjJwfP3};MW4H*rNL%sfokP+>^`Jtb z9@PC`_2{?Ako1t$^YCL3pxs&%j+2%mQSt<UUuJ{7V3F!U&W$DU^=Vj=%p-tF0@?qg z#&=O;x=<q{J{z>s4pk**(@rXa`Y(=u$c=egJQZ;)x^8)C$DTbt?bNQ3{5L<uv6_F} z*v#|ER^tXDUrvRNT64S!io>a1<A#dcWI^;P$0jY|{o4GhVd5dZRjSrOy?$*DjMH$S zr_D}#tL)0Mp5pA2I6M7&(5B6EYBLo3NsvJzmE<8jNzvr9tyz6#ZA5s-s$HS%t9rqw z%~K8SE&AddhO_9+JK`6SH#Q{WLb4$YN8mkSX_sfkIlWjt_MF5jvS*#w9y~I1gx~)3 z4?~BZ-44UL!H7Z}$4?3M@ve+qA*Qlhj6{^?qk4*``im#|AT0@72&|ZB4y<Uh8QQ3E z)vgs-Z^ZNZGK>i4y8=fHL{#(^Pi|;5hN309iyy_xgZ_WiD@;G{;l84({S_bfVoFvG ztEdk>h3r#wYOA=e7g|s~E@oH-$ixre(mKA^CuXeeeXHtU^@7>ZE7F>UM%>2eYDjZL zmb)v+*@~x`xuE7uYx5ni*63{ysJbHjTBHB-cGXi6R;|%pY_J~vPf@b;9=^0Z2+oAi z;&(YyRWlgVxj%%t(l!2!x$e&O7jD$E!YTzu>TP*LjXciFeC=9qyY4iN1*}bpg$#lH zRjK=1-@D^Ke68pyDHuYJ{gqXZFy>$T%tw1O@`l+Y$-Ep1L-Ud=-nj$jub)Hh9R~>x zXNU;O>W0GNkEz~c*Hm{>e8;Y;wx;-xeW`jlrRUgZs(&Zjj<xag`H|J9RyegIRjCk% z%C~Z@9MYFX2&sF0WXXy;)7}L8tJ<KNh^SjRaSfQ$)uPWsuzXBlIn`%&Asszovnl*q zckz^$b}#xrnEqSf^hY*5;s(>lDu{i+7%Z{w_P(YM<Z17L-DA8fMAY>cr0NY5WLr6q zdt$6a7#Enr^Q#5vxb<c*%!EJ0NRJDRbj8?SsIlLUaB3%xZ`+y0h(GQVm-o8C2w&gh zJ=|8L+2hwvoKycpGh=?ew{;Jk?dB4BtcX(GQ1vLsYeWbFko407R!s0$?spDi@-C&g zB}~^tjl(#n_91d=*BAKPwX`P7FJBj>euo0-5b$O(HsyG1nD4ZKG3<Rk6Wiy!i=)oA zdbRmo?Ojfv_E9fgUOH0mr&sjQ)?=4%+!tq-Q!uDV{F5Smb-Yu|IrA9nEJoFO2L;{Q zN4p;K(JTBYdM@qU4<q4tDF}0cH~R!}_zehiZ}y?2PdPrR8}j+o_D>PdOgo2<s=m3R z$GE|!o#<5NVF4nwD08jYlPM?m%u@Jg$$fcI?z~8EUW9cP@0=B3n+1Pgvu&0PE&%#W z%B>vl%o~>UBj=?zc=Lu=eFee6HJWmoA*0g1Jl+o$K8PXVTq2Qu7$I40vzSBARF_># zx-bex<LG_iwDcZ{yywXweo3T%+8EG`ciIEk#$yk^_jRTso>~{-d|U&kQ`>B{8?124 zmtL^K@n8D2Phm&@C<S$ad0LR4R`Db_WqA+1;!LI07+qGjY)#OrHTa6tR(;5{{t7$u z{@LQz!cgIj&|CpmI6y=OOdewW8xsCp|L#-89TK1TwdEPsQxjd`3)-I4Y?Yh85%l}T zRIjA_ZuSK$J!Vr&4ZT{K{B4TXbs-c*mc*KkoDBYx{wnP~9{c{+^^upZUV8Dl7s_sY zpnPdVWZgU8x5?CXskhvJpn%5b?8`2Ff0Lx4eJyw;ds*Ir;rx*2R(e)zt6z=ZF_<ZP zCH|$I{Pkn9)HS|Il$zuyeuchbUv`^hi+uTG^_4siKe$)ABbM%EU;0oecpE8#g@SuO zN1QIfCjz1C47_Ex+(<%f1+8qpGgc?aC7fy=mvJ~dX<;&KM>Ukbqz~>JBA1$S75DK` ze)drxI}M(ZIOv8L%vpM%MaSzQl<QAI!N6cj|0h@i_ppBmm({f4BpIr)pHMP%E-2=u z2KJ@xk*P1iB1K)A36s4%Y66t;<h-Pqf=X#CgWv`xMilb#?mEhgw5Qn`+H3!q7^Qe^ z{2Qf{A8KZ8nuV40Qq2OZ6qbl(FMH|{X+qYXs&Nm!GM_FHi)%~wJ_wt|={r5p`x#`a z{Ju08zbt#^fvynq+)y9V?-x@gEK|sema_2jk?3di?WUcq)K%Db0r7U~j-0uErD56a zH`lZudH><-$48Ib(j%kxyk(7rDgJO0WB^Apdml(o+tJLq5@pyNZr*^<-DfX3W%Sw2 z1Qy-oP#BgYX`VCxf^@mJqmHz@ccy9zzOjr5edCu=?T_LcRoxwO1Q$9xijR*)xHVF` z6v@Y-m8^<M%n-t-@frPP+I8dwDW)S)*%(p)AA|}B=TE#MFskq6OQP{hhhEu;f4-qt zR$USq(8{cZBBCG6!#~_TgNG$UuVms5C6n+^H}uLN{EI<#On-aRU@Pdl9dzcvPF+v_ z!-!q#p8+EanQw;J%gT|c!_D&U93U~mnZwxFNL!A`%0^jp#5Q(V=@1Fx#pkf#!=))W zjx^s1wrBAvghOYkaq@4QA+@rMHHXv1;>=FQ$MmqndvYQHK^9I?(4T3Hlac7h_c>?q zWRxDlj|hJ(=bs_<#fp71WL}o?=D;$VcITh~<IIV$u_9Yeq?Hw0bE0f43#W)7wwz%V z0AL6U4al7!0YqjFLm;)yV5}@>%|QqUnQlfjfgrLcXE=etOp-$Y28|rn2$kqiAb6uz zu953|Z&Yn|ip21s13S+V^Ek-0^x+lsbHC!153lYcwgz~G(^}UZ^6Iwh7G5EwC_kAT z<m4cL0;xM>J7lwNPKF1UWym$Sci_N7rIzq25(g3~`xf(-2^aR<hCyZ%BbHN^@fV0S zre!a233J0u`V+xAq|Y+^+VvOVbj4WS`Wm_f6T_##w$UHMG+b38DFYOnKf@BDkU=zz z>n{$heJo^?%$;NAz84Q?GuPJJL$74NyJCq93nkzR8HIUa6lPV>d*(Y83d{8Fs_Sqe zC;2p3ei%Poe;rW|?>>mcgJ19f-;RRSiVN`y`Ej#H7s9wsM3;R+g52l}E+f1u&(`j{ z{-Wyl(7Og)bBh?)imy#M@Es??i!4t;fYeMaFJt3z<f^m7Wc5k8X$E7;p<yIsvp)?? zxlNQCgAWSKQP^@4oN=!?XMlDHUjIA3Atl7J6^L8)TYN+CY7s9hc!PbuKXh$zlxWS% zhV9uo*8GWXmWn^<DSBN2LUpoqbrsx9<a2A{-AN;1C)Ti!RsAZ_jQ6q7N>W}UowOjU zbatFz4A(*XBxQkfxBHE>T&r$ZsHxKYNEnIef4v&AyU<_13a;CYb9M1RO8631V8WOB zGk3q#ue>de2!H9iaka-Dyb;G(dhn&?VzA;$FmZNdX}%uSx_-H8wFg(l4S^*gEd4EK zkb>5*U0{8xt34xS(3qmN?}HWp5m<8wN6%xVcFeUP2IRtLOkx^1N_<S3n82xCByN&c zu_9^HRh=rbm4bZ-vJpFj+5@aE<~3ar!E%VdbNAMfA=FWdlN|CXs}qBlsD2S5aFa2S zw+Qx32doDUSrsUdh|#sDlF{`s65kBUt7iBCNsHB3q_7KfK1SitgfuKHXR2{`2#0bq zQsmNELJrj|3JOj~Ugz^ZMvUmnjA-GxOLb@JJatxhBps`>v`9%6j2Ik8h8!}9DeT^? z5BU*WMdpiw9RqSX1D>7Fy<~mu6%%Z^CGw=V<(UXf-8vk0E8$Ges@7f|30DdDw=%*P zOYK2}Gd|I(E=^4~F>+*kemRyiF;r&kFqOHF7Yi>_F`wlxZ)Qs-;}Azk39TyXBYi<V zdoy&2ONSfgWyZvfUzQg$Fwv<ZdM>4*ZDR~itQv$EQFkpTLU<MxEayBq%KV8|R#G}R z74~$<pr((NpS|RXr*@55&xk4$Y$=YqN-XTW?EAq8pcfbz!X8p)FDsqVBECbc5m>Q> z<h}@vUok@UW0vu*BsniZ1I2&g_{U$`HPEwDS?99JC2qO6bO@8DW`*q3*3bG>uSgOH zj4Ij&Hh?b|yJF=yr*dRBG9o}><MP~Tj{T!dM)7WK>5$jdIud-e@S0hTW;DE~NqOKC zmr_1bQoGaZxbAd{J#OWJy>;&&JBWmP(Q*YTUIec|g#Op?fk^o#J@$4JCFg6DHTt1f z{?E@`{5_wMp6Jy$a^g)Y>#zT-iGb{GS)7y!Ji-W^x|DU>t#aTrLcnNH);^9`Ls70K z(nIOYZ+IAicZe+A!F>sizj96Eko0<Yf-sIe3zU(MJY$sO9>9$JRffX^1~Gs5G|MH> zOi0D^*w+NvXwMoW!r^TDuJ+ZA<w&+QhjbcjZL*NO3Ge&oRH2<pHyU4hJ8t?5p4qk% z8TH!IrANvHc&w5)tdpsdyf^yG!ll-6W>1e#mu8KqFCCn8yuQ)EX{z+U${O$E5E)8S z<vf<4zb$KgqyKKa<rbpIG{Tiy%??@=(^$r-N<#0B*ST)UD&$n{Qu5*2<8_vwE_-!p zZk}&wNN`W(aqs}<vQ~fjvR8zIw*)NxRelsWzQ^63;B6o8dh-mC4`Be~{Vd(JPPoxj zS$bqb{x_P2r3$0}rdy*3T)bRQy9sda;|WAD-7+|Q_n7M^87b!ax?96H<CG=ca#?0F zPOpdfssGQJW&}Y>5K_T*i(MCP@~JH+SRW(mmXv)&7MQ?)x|f<r+}v^sVEOEC6UZJr zbJp9w*s|sfI_0%j`sMYFmsrJR)qMkc6y=Jb>cbeD{DZ_H%EYY5#3vLsg*6=7<cT<& zd{1eL)T$`69_=lv9GsXXwJFLF-$&Ip2r@-JRhpGtHkx)S%G1Y=Qq&bKj6CKHynGc_ zqG7NM-vrKd8@?(bAXyQTWUK_=A2X2}Rtvq<cXabp^rwMjLt{DT<YRsOAU{74o~&h; zm0n(TsZQ`{JysX6Nt@*u<0bOlBcY0Z`l?lz25rbB)*lmf{yeLCn7HjBg@)lD2n;#b z!=7pPc$S@`T^B%vC?dHmgdFOI1+K3p+VF<{2Ry$E<+CB=={vg(rQ$E)GNPYz6)&wE zFl+cc^wRQqx1klG`1vexOVAb*W%ypQV6-rvEo3~~FO%OEPj_f+IE>tOz5Y0Y-rjBH zvi?|i#i8Ic3sgur&bXSp;TE)A-_7<S31qzpXKMGBwH)cLKo07DAU+L4*xGH_Tk(F7 zPig(TFn<+Jdu5%|T^Pmx(e>n2em2N!>{TyAmyVazGy3SuPzu1piap1aeMXd4fz6jk zvi)-r$dt6-blMwSjor?YG+yDgH|S5wtlP&R2P<MWa7ZESC?I)QkwUmMR^^L<S1l3h z3Zt&Sps?SH?<o;7vA%G1)w_}V$_3)ubF7XZZ~07CXs?`I*nSNIR?oR3_IM-eeGwm6 z9=Tf2S;iC83PfwTFVrj7zHWoTOK61TacEq@naIRgdiXIMiR_h?v<xn6Nq5IYnTiLS zTC&^MI3h%we6YQ)@kthq$1Ktg7G{zDkPK%;p(*v4{-8i!mYu>Rs^|0vWCZD!9ie+x zzh7pKz+nlva+PJ@ZyzSOnuLO`ZQ&1HdqcC)8UE1qi40A)Ob_fogCQo!w><D$hu#c# z=>2X(mCzydN0{ec``h60Re?sKw!l`QHcWa~gV0={HvAyj^&w*MYIsx2*m~CTsSF6T zMNkEoSi~7x+g#7`Q(g5)tbuf{^}$vM^jXn<uDt34JmKB;l?R$#@5{WL)yI|lxiY^o z-)EfcHBNF_zbEM&3tOhtbFSSoezB>gz>U;OZYYg+$?7?NQD7H{9;bi!htACnxX$<( z_}nK7xiGp#uFXPx_TTqK?-D@QukY_mU|VQPSNHcN;QXyGuEua*NKTJol2Bg)cA+l; zgeb#o23S)7mM>zD&zO(teZXy%)?1B}dq6~NPt*rJ#z}##vcQW%XLKzESl(+%l95F) zB7sr&A`AWr(AU5S40<XJZ?VasGmWv|w*ec*tgSr}^{O{+!8Uv(hh?%x*7|J_zDrXa z7@VylpS{FyU)Ez^Y_%^U`45aFpB=<nP?W8BfG~LPfK87_PJpSFcVrnYnx!p<!j^1P zi@C&J>C_HbSaSWfOu_^LZwLs$lt%L)y1-vU_maSRSzv(xJqU8WtQqcH5Fn~$5N`$I zTps?;@>Ym=Uk`t0d6Sgo_{D*BvVbL20jpmpjGTU*FmkS{;1q|>yBK_BC9?j2KGuT4 zVa+A}xz<&&!)*4|)o*)L7bS4Pa&NnA-wwwbzTsn!>T8ZfvO^aL=tx5msOYX|7k+{B z%Ef_idK&-D_HdG(DU_d+`lrx7PUf4!cn3uJ$nwV)ofSK`KY$eJ63eqUAqEKZS|l<& z<$Ar_@C0_j6|fVMYe=9jk$%QyBK)DtsM%+)Jo821R1bKKwSXcm6wv+w;L?b?BxGGc z{oN9M%B^})e8Vy?yyP2&G@s7xV@PZIh4;4huxnYjAq!HyP?(=%`5=@il=OZ31FK?l z;EaoEN%v)uq~pirz(CX62*?xy%`!OzS_C@$TQB%G^?&Y$;xtD@5Y&&IW!+eTM_?Hh zI9*_w%r_#9uz_uNxt#vz9<qP4yacBfUAJVs!Z#p_*9ewd2)<8yK~hGft|6%yDA&?^ zIsybhM+kGo8BvBuEzfitCRuYtNIVY?if(HH+ioBgjrT4nmc`v(j)5_-1{)q=N&k1D z+hHW=-xKZtwkS|U7O0ZTnuV6-mjukKQsLs;Eb`iyc8hx@`XN^;TFE}&A$Z}%4kY;6 zc3B`70bd8?1Ox5?QAvxU2rxRhac;iRewzS8?nmIh=7#E0A3@T$&#>q)2nh;&-;Ah0 zaW^lUMr+=B=|hcsL|Xpt&G4~GaOYFj5ozlEn=2ctmszFbt@(q&iPs{gDtMx1EfR(o ztmJw)%5dk#_zC&{aH0u<u5L+S&RvAz?W4YqXgGo(S*hi*Zsbn=J;51-QH78&ks`G9 z?110K*6@_202GR-_5!*(#OEY|#X;DS^^?$=5D5{SG_FLvTwQkdwaHpj0lzAC=p?Om z`-oMsbALmVYzQH%HBy2m^n99vPlU}Eqc7}sbCj2(OVx@}jnv06zK%}S&_Qr)BjevK z=~42UUDg7+rvQDRTt{y9H*XCOo=_R)^t5i<fT%$iA0!_+@zMM40vf4Eg^KW{{&4@7 zuD^!93{!SME#Sc2GT0ePE&>!<KDudRsiG|Sz#+T$WO!eIo;5r&w*~TK{U~B7!&3yg zxdgechk+|`6hvRh2u9Xx<i^AKL3|Iry7CCIlQee1DCsBO9VKvsM_uDWgJV7$EZuJa zBU=k25`OJkOFe=Bh1%yKq(2sT1bhy_kelWDM`TEC=WaV+v;{gBFtmq%gLJWFf(#m- zTnaSFYZ@9@RT>;PFbBv$TBZlZ-u{7fEfMe$LO{2KxDHWXfql&-mYY$$!Q#9Z)~5pZ zcZ&}Tdt>YNgjM&>h23H@$g>4@8RiE1G5y?N<*_<!*hUk+EeZ@L^DT`Cgd+WLA`lYF z4GDy)_cr!deM-0CFTUSVt&0lrBGMav4x*+L1H#bWX?x<T*mSQGhQgIT`-+pX6a4m- z=fCqHm`2a`2XDk`%TKblZI{bddSJ06FuN#1kIv5)H<RTqRfaJh5aofs4&4SP4)-CD z)Ll3rYC3SJkU!Ueyi9>*#2TL=A{qwogTSzE!*0y1dl<LnFFhFhjnD4uC)ZhFat$pY zIxHZwLkq}jJ*e9tl~^>+?GJ+Shsi_MPp*L(!k1+;;|pg*O~1yfeu9%wv&i?^V(<3E zZt?mTxn4>2JJB}qkzrKpiC~@uIK?Xz=oUN3Te~Y7{edW;p(?(Dn^%~lcXgXv@9oBW zmY8lsjkzP(PvGd<dwONJVT&8+KiE$$f&6}am(%X;`fU3(XF+aLTn_wQt2cX8PegbT z`!p`*#7WkV#3gY<&tLMWhA9Y|LDjr#@(tC(h%jE!U_W{${ti9S-B|2JuHG&(EUswe zPl~#7g;fogLwLbN#D?%OOO&u74BaJIB8eCwa^3Fg-w;L!GU!}P0ps)leTJOLktJy* z;%!dVi)2KJ5GVU>#zUw&&=`0DNQW>oIe5>a95^kQ{1e~<6K>RQ4#VyecVm)By8J>9 zT;^aa+%~}M|Eo&Zv)F%v-mi4sOXGoGVN!5c6!>f4c9&r}7E!2<!LHmb&%vwhDW5Xe zS1{IJkku_A?CTW)G?05n82yCrW>;Uc74W9*iP@7qs;9AnKWN|WQKc#xPqN-^mmBtb zREy-EHjhdp6UaQoQ%Ti4NxfV^BNwiSvMlW<HT}pSUwD`5EjPP>4K7d^ERFjNBQWcy z<`9p-H!VNnu#5y(6xhSuo5HNH<b-G>Fy|f;D*qeTGeT+TBd+uFl=`kN!>!-1L4nUg z_zlnI1fhHAn;X3ny4QfdeTUWdN76TWq<i~=q+?SeTTZe_jYGO;i0nM=Q7sXOOyKvt z!LQ``KM{$k04XmC{+=fILqx#9uzM*@e@W=0D>oRQy{t5@g;%VP5<ym}{Gx6f6!5FR zu)NmuyOqIJAk^b}l*}BF>x(Xp!t(EKVTgVA4Dq%e{p0rxvF5!H8gR-nBPxUm*VkQ| z&CcKPHT_3-DNR4<_kxV;-+(j3?qcqaKXgUgE)ltP+mD2o-Qs?eTQ`e|-1<jJXxagl zh@%#BLnRmYQ}3BBUU4Eybo2{BFoW|)+ZV1y_ege{VK6zH5v4!T#WNZ;7{Fv<0t+=+ zB5u?!3|FN8svj_mEQDAhef7(;JA@7q9dJ3Vc^KTHUJN0@RFG@fYbqYvw4JFy7#8i& zBkwa6<EZ``(IlpFpjnvwflevefAWJ(So4Os4?pH(*+~i1&}^#4QJou|J!wPWbJ%(g z4zq=XK!;TLdVfD72(+UD?9sgTvSWwA3-m3g!3+4Q{t;=|4f-P41D~qT`5t8hEz+Xf zvHvxD0~t3l$ptBXAQ@xV$M=l!c0J;J-ZPCqwS3xDf=Mi)OxmH=ZI@Ri%)(lex0?|s zK*Dd1Bocn$Z2=?h?O9ERXPm{uj-;jZk4Q@w1R-HG^wWfa@QXxPU=S5>Uzbxm%uG|5 z)lHfa$yn?Ni`~wMbyS|=C&HL4*RB)C!0_(mQ2&tVuk?|n(YHPGWbKiD`nfDT^KVc1 z;q8z5zt}I}8e)adzVb|+Yrhn07tt6EizL0)yMEW}z=yx<wf@7dik2Y4w_dsCDT;y| z0&sr3UUXX$;)Wr12C?D^E35_z#@Y(9$aGBW=Q)9Z08inFG!w^a#^sGm696^*gCBNO zPsL{QL#cBPIJiS)<@tO=w?t4{`C8^$n)@pD1VgLwuYLCn!v_IZmtjY1Et#NXv!>tE z)iWXu0WmcX-*`CZo`RoRwsnb-428@q@Yt5QhJSbok-@n9sDT;`mj<fRQPv)9Mayj) z)(~Zxes7=ot)Oo}RA(n!H0?k4RQ8_hy)MJM<~PV5p%_Bv<(&!2ywr*PGr{trFz#M^ z#gT;kygf|o82Gy1-Q~G%|LokJd3Wu1{q;t8u=HOF$id5<32)@Vn(sjfm;xKH_(H>I z|Jx657l(Ul+1hnarIufQCUYjr#>uUm4E9D;C8F6OT5KV<lDRC2Z8vkxOxu8{ClabL z)KAI6h!C`c)9-2r13UT<eTeW8fn<ImD{?=f=r<BV;ok~v$XZ(wTH@<fZ~OYedHZ+Z zbp8DLC(i9z{qoJ<1S0CEao3tIaZ~Wylk5*~Z|LvgVWEea3kC}Nu<Z$CA4cf&OZ~xM z>-+aCF@{eA6<vl;{g`_@1pLD)pwenzwJLU`2Ym5Wq(2@@<kI}1;6MUjj0vwDhdrw0 zLp#6psG0|n9)<xARNf1C;QY_^^y{BMJgk(u|2fFn5)F}L6?jfG1nyOR;7$ttD%kwQ zhgr~LuD{{s*#5h~O59CHm>*O?e#XP>h6p9ksSjFSIZe(mU_`903%n`?J34WV1;6RB zmvx?vzS16V?^pp;V~y4D2y@U9tuG^UT%=!&RY(6yH<5}ka+i7XFtG4<lhpO%y_58^ zYgt#Trk}$GUh5*Cmce=$BE#6bbZ1%6Pj{|80`B|q@v^{?T~CKN^2<_uE%|e5c$@;e zg|hd|h08VvRsHvPEOnM8U51NdG4fc1Mdfd%%IzNt(sin9QJ3NL??nN9jxbA}`STD# zn0vB|2(o%I?$G`+0TFWX+&k=-EzfkBe+Y)5bS>#;doH^GQ9pct7w&|;dv?P$H<6dd z=BY=Tw(IlMj}WPK3v@nWB451kQPs)Aq;cG%x})m++N0VkY+1vN=%49t#Ijkaw!d*p z7h#z{u&QsQVF{#Lf*7NM3_|$b*?!ap-VFhV=q3;-H(f^<;BXz5;w~%Mc8SW$*rMdf zFhULQ1s?A*)chV%u5<xWI0Hi^mbb5~Zp-@teDW@U)gb_}mg*-9Bd4Dubz<Zeru|-` zIebIlqOP64<D#yDF2nAMZ-c8AMhX)i)Onx|Dw}8E4{*;;+2y%`mPG**NHXxKAPDJA zzoSI`wBJ#pekw?!;<z9y_jmDfARiC08wn4YYh`%I$a$Dkgx%Z~)wl@$lutyj8pl-J zPpz-usyEv=$?P&JwI12Eow<}cw<3i~ZT2ffUIkNjnZ}vOrFOr9h9x+TNiHGEXZu0I z9liD|`aep^IX8=+fqd!gT%0_n61{PiK3}>vw{=kHZjXMl^!;3OZ0N4bGD-SlZfmq~ z2d!zI)O0u+N=f0v@h-FUq1*}pL#kQqnkZeA3#u3HrUr7QQ*yDN5bg|t9BEoEf66Ih z(nc4Z=?U*!!+qQ=@9!f*qpV-`TOXU9EIJ5RKk;yMxS2q1c_a)uZuu(9!(qtD-5%G3 zFy!Q}%QZd>Iqt9lf2bdF@^XA&To`h4w^%<e3^}=*s?Q2TPVNkrvG+j!+AqS{=7~zK zv4XP>rII%B^`f-q4wks2W8>`Wh;Q2ZJxPZw?92amIg9`QDp$UmgX5pIp>l9~j=e0` zZbkf{SkaEbnLPK9t+Pg1Jvx4EM>0k+RS$!sho$P$43oqu6sjC^X6D+N?PF8Mzz$(k zZi*}=cW2^>uzuIEnzK6_b}JDdrL2y&?;5ouZV@ke2xpdU+RlT@{dBUD=aOS8wXf-X zG0c2=$I$Gu(s-Wr#fa8+jD=y`xH0u2SEf`wvm{62jA8J+GUH6{+T8ZBSsA1~Q<f~t zv{Qd#*KbYi(=I)N1Zw-WJJak03_{EG;^NiZ+>(a*D$Uepv3`W~Y^+-)a;liM2jPLU z_9It^*gpMQtv;hjR9FO?D!^xMd(dl^!r^};!atH6#_7*p<He1uIj6T5c76?Y4K_K= zGa-B89YYpB9;caV<)rR`qQZa1mL2rGoc2dVD_vS3Uw|0tDcbDsMSNOz^_Y!mOm+!1 zU->v)NzYf6&9tBYn{w_<d-4N3f5^m3QnV9T%^yxNryuBPiS1F!+aq3`_$ou9L5rn+ zlQ{eP0nzbPvQ{u!*pFE5H!JQ0Lk~Mx^DXFx%3(8;VubHBP+wh^P`v@}7rn^rZm((B zG^DK3j$~-7>A3(VX>RtIyd-9B3H1tA(&S7mbG3z!8)w;MbA?9MVO>sUihO-$ihA6* zPMn;_%i1%Z$i(dv%oBMvT&Nhqjgcy0#MT%lbFX0SmTvZ}Sz#??;FV`GiBogehR)-4 z3~D6s$u-lE^Y-V}oZs6E%gQU48!pn-fXGnYFs8}CRF_bX!%jNZ$`SMmS(SDSR=+Z^ zgpPeu;v6JW>vldUSPGR)lmgXBn=Vxg05{9X1Q7N-NLa6V)MOltW(rnwJ>Fi;I$z7+ zD_53H4>sJQ?W8Tc(3TxTLHp5`_@1`3HahkdRS7-#Nr`)qs43LgMjI1qPpwPZ^n+b! z5gC_Ai|GHT)tt`T3*AP#t|2)_#~>Y%KzC0vrWghtPtWz3W-{Z5359N=c(!h>F7%y_ zkpRH>(Z-C=@O1)gsbCB;eRRXv0qHusE{2>s3iW_`d1h(r<Aq{+5{QPL{`jaJ17lut zMcHCWh`_Zq)s<G$;n+30wWTk=dpcCNaSc+h*>X86%cdmk7(AF4jl_}Ht^~d+wa&#z z*XrCXLccVPxD<x9U=63^xUqV*$uyBeKSFI6Qz>+ol9%v^h0afz(%3;Sq4~Ti&zAv- zsb!$&RHm$i+A5t-^YX`Ge^T|&SGiYJyCr1;hK62wQjDk!=tDV`V$`r2j+eYj7q&!C z<EEp6<mq$AAVlf(5^54VpPeA7s#ri*idVDk(pO`r3l(hTk`clK-xpI^$`o3FV<H5n z{3(uRdP>0(`Z-jS@?6=NRVmDKWH2A+D#cqR?XMtIHTQ%H#EO67=fc3Co)*fn7RLgV zrB1>tv_NZ>)!X1i{7A;~vt%n0ue<Gvm);jUCsS){)O9eMDXxMs$Wi1y-JyB7Ud*4x zBfp&A`>-BAdgT0#@fu=VBj-cYEWW0}DnlYLk_!y!;3v~lNFApV)dx?e9mX%WnANW1 zzI~Iscmjgeg@31}@*E!DOys5C4`(VKq^60Ve+%JX(M7)P+x<V2PatZXJNXa^<oazj zr?`FoNBf1yec#$6sg#!F6Nla+HbRDWOF#l5Oo3ey4ElIQ$OFUEZa?3ybo~d4EPCx| z7%p_48r6Bu9zW9l@`f+?I8@$D+gQGQH5Y=uY;{9Jjs2#qa`2U)y)0-?i9!)%sPq1b z!H-IrJ67WUlSo~;T4o(C>KRTRuB|K!TKz14Tj&407eW{w5rfm?cT{RDVz@h+sGfPm z#SrTRhW{wuzRdMrP_4_fU31)Kb{(_)D`@%b0&Hc~Cs6k1+pzYGu>L6)zf%yXP2&I4 zK>YN@N^k`n$Io@PTX}?3Jf;(02yXg0amW_D;5FagW{__#<#XSoV{q?gBieqBPirv$ zJD74JxuxP9p+6(#SkEnQd&r950?G1Caciur<r~Lx4jTu1YGF++-@uJp&uQQ0mLJa6 zMXw%^BAodfspZG{H4oYLuB7YPhw9)Y+ps@6eu`&$sx^Cs$9nWy(mqVh6tK3`!y^^P z+uOv>KeB~?<X%<RzWDq*94|UPWr^aYcVw@cvmTs)B!T()ph;eor4V?bMy?U37|B#f znJ&;Urb0<ebiH`Km={eFPo}F9D%sWI&|IkJCegfXBwLlsEB3SdV}*G^=GD*daGFRB zLdjPmAxDP{oV;66Bf2%1%+Zl$o-s#8k~z9hv5P*cXp`?&gyv>44GEjsWo$3p7gwb| zuMiBJfTz&BH&R*$u^Q9q-byNAEN3YRu9NA^62|2NVublfW??#kJzao3y@U$9LmRpb zy}OidIdw$MorHT@FsQDpAS{5)VZ%>`YwKsSyi{N+k^EFXpO4@b&Gb2XilamBL1p)O zI>(&6kMRItdY8Om?FQ?DKGdeJyP=ln!SFWKrJ_(NEQC=XpUU&74~!vPODAf$zw`W< zAMR&5Ne#R-RD*s1`ADFHs=`DVi-2)MiWhaT7R0JYkX$HSJ6M-Pa*ypDiE%b`N@GR= zV<`^v`S|RF<*P@%%M}Q?vFa-bhH?=5CAus(-ejrh$6e#QT+2odB5*xJ;x<y=Z5KQT zt@=DMQ40ab|NY)IQFtT3H)<m{jnTf#A&$j39WL2At3WIDOo~z87etEGOQIU3{gUXY zavu$s!=`bN_E4w(B~jxhZqzzXTf^0h8g!lx#`U(OaWmJlg&XH=X!p1_AgoqsJ#q9G zY!=x`LA_1l2o-eO+dcaALP6ANbciw>6fj&~%jp*Uir%J>8J_WU8EOLl5n|d=%Qenq zbyx!(FxKUxdg-lnhv@esX`?CIBJy>OoFHsO8Lnlzc-_1*gjd4vsS)2gMAr9D^@QFa zM#!_Ii0@g_44J#ytw2W?X%p?tWR34}86$4<Ox^%24#J3qo@?LZbn{>-Eqr6?MJn;e zTCO&=JTzL#o65=(N++?pDKIm_(l~{6-3dAX`@&zzC#3H6f9J|SCS-3-esAL@iQ}|L zFeT$trz#M$_S=0TZ3TJj)!A@8-%i5YE&dCXWZn|;z+Lh;*Ev#Sxt;4tEo*detNtEg zOD%7zLCzcR!ZPo|a)*=Cq~SE?B5mA;TvSv>EZwwOUAuPBJegxV_x{Ha_g|GH&66Gf z*x}-MgX<N-`A#@Qnn-`id-&-u`MYp+2v;YrzLA?cI}=K$F)n}5MNt+KcS`6B-WBiQ zo=*~K9ikIkL|z@?l>pdVLVlQZTbxO$?xfVYxzS6QhB|vgowAsLraqt%HzPwSiWKTL zJ9csbwHUwCuN-dN#Q~9i=bPzgJl<WD=b!LQ?Cji5YMj3rMQpm2xHi;8SL4KC{z~gE zvUvC<j-H)Y(k^>0D`DE+lSn2EV!#l^RVkS*WZZUhhzgc5fX*J_iI2aQ_Yp*?>G;t< zCh6(dYLObl%}_3cmCz|*FMF-%i$1K8>cHRb+R7p>RL0crDkPY3*etK4AV(4PRp42; zdkQJ#@mw9SQh}Snonv}hc5_p2+BCGgJsL3t?AH#|WK5=Z?jzn1bLfhz!L4#}FR@{( za&Kbrrjo&$2Noa%1iM8LjCal45@tv21O%VjDhe(5wPWG3SLc<{>YC8{TXcs=&%Atm zgh2wIgCC>M|7Am7D$BDK6lKr#;GIDt`EkIG*oiz6=)pZ6UU0fR+tmsf-7s#F>YLb2 z);?4(M$9s`&MkufbLd&o`+YlNBgi+1S_Y)#ejkf?@H`Wr>k$in5s<SZw~FhTk)quN zHO8|uU@)|+pqV~uJSQOs<Wv9~8}K5(QBf5reVhMk1C<^>mmgA7+)5#T-R@$Z+v6<J zMo*xDMK-`9|HF>WY~5GBj#l!(-;wUmk?U`B>Y0ksBjrVFdT$m_pr?=a92{NyQ>D>x zrgrS0XOz1N3}3r`3~n;=v5J}+V+BnqMmuvTo&_W#0tsH`r-sJ4h1yD@-ixSnYzNbJ zVj2_br%E0Dq}(uPyoA@CF0Ls!vL)#rB&Zfe9#N}!x#8T&Eh+r)d)~Anf)5)&sIocg znfE*{Le^vh1rONBm$wX5*Nr>iQ&922_>f@q^3W~}NVCXrd;C_bf|{Tx+efh9R2SVe zGUEJYd&A{w|L2|RkHj~Tr1FKGTVgboQ+6zo??g1KENW9hO{4iCq^*DYq<WdvzF?L5 zS)2WpjVQEMDD*yK9#0)DXmhkqpysSQdC$k@Z1VB?Rw!KTFYer;z579H4*8(mJdt`* zUiH`o7WM&1N{P*=_4wAJ${x*Xq2iQ>cNOe@UaX|T-=&H1Q49Qw4d%*X&AF4Q2$HnA z1=^84{Lp<ZHhb~HgKn5Eq&-qYgIAdcqTH!s&DkTQ-0A0ub7FKy<hiI#&-2oSHMY}1 zTgoGF;j|i+fzBWtPYvdy#hS01Na;4j)~Iakh+J4{Jr%SjKXRguYnGNZuQ(TlTrq|@ zvuC%$65p&jy^$2&RlM5)AR_j`pzSUQZBW8O07Qhpdx2O_3<gnh{nWwzu&$;9z92G1 z+*_<%=u^bLa_esuuBI)WLGyXC&O4y{OfgO_e&YEB<*%;i<=u2vK~GD%uO<4l@)phK zWND8^o*H)HKFcYAJCO1sQZmKZO_Q%s7RCN%f-uHDp`EM@m3I9rd<cm7-@o@uj7dSX zr9H2KkHLU{0OkEGT3GWfNtAN{HMN(xM_k+4%#$-nfnAXgx5`^XG7)@?-sjfu;gZHi z6CRpkpS3{ZfC=7Ga2Y(vLRqxcUnZu%jLh)?7x=TF%E6;#loEJ;RiQ@i;;?#a<N+~R z#IZ)>eR}n>miyWCQ^xWSx%3j`h%Ld<N<6mNxU8A^gtDN4Y(gd}$%Vyb?$hzFkedZ$ z^(p!A4XhD2N!i)KTjcQtaycD|3j~mO3VD3UgI(kzEby-g^}hTndGI*7OeQK60t?yL zKv<J)rHR~_oE|?WDVc%XXc<j88o7NqKX@8)lIvrbDD!eXLN9{JyGBvzq3w1^?nHg* zg-WBFdxAE>f0(N!`zpsr+$Pd$T}=&Xuu`~4`54IsK_zkGe$uU@q{Nx_Hu7?xa8bZJ zDa3y;o?<L-%l~B*gauPVB|jp4O#JlR==hu!C~uly8OAZ`gC&`uwdG?DStx$qCu*z^ zm2VVT7p=uZU)ok&!+ARwk>5_?i8ZalQx`qxm5BA95M);$i}x<tI`qo=y&RE_F~<7m zF5)@AW<8eckGYl#(YG5#{>4>z=3l%TSGR#w)`_GXi?{M>{IsULi7O%#BR2AaH}%6w zPr~DEQ<l3qq;8f%6M~e~@<2R+7fHrG;u@Pc0T25W1QR>d={#BMg(1BVsji##EZNVN z-_IrsgQ!bU@`UJa5_Dxn4ZX5+ODCaUw1ahkhN(K1R~;=N5q&U2k|qn6dbH(6P?1~8 z5>HY*7bE8xJnirZ{9ip<c&^AbV~qt(+$`8fFfx0_6Qs#q!bQ#>wKpuY+sEvbX4!45 zYO+{+Kbr~x0qTg~3PBHdo+UjKz7&XYpQ532S%a5N`q4-HB}W=R;?gse6;L}f`hNy% zFQIT2>B}i{$v<~mi+@Lj3qPxi_(Tx#feQ5puu1sD^}^&xcPe+2mMh5xSs}>h0&PFe zne4srMaiBRJy#!}o<N8NCxu5zf&B<z8=`wS4F3P9r|<t=4`d}bHk;URkZQwYy?#H( zKghEJeCg&5X!U=A7@B@$975k2sUokU*v(nR6k-w%R`7B9PE56yCPK)||Ai`uET6np zbWYSQvd&wD2O-IP`cDYp2Xm4#Jn;)xUm6kpJTRwRB*=hiM7D%ywr$ZCeN#~LKL3dJ z00)Z3QyvK`t%86*9r67ZkUC5y<+%2gtZ;oowgLQr^>Yz6%T!{;jkCmxU;@0nYBFvz zO3L}TKw~h&j3*e!{(jW@MyW)`3Xz~h$#}Zl>AY-dAS#-Sr{Oi1^bRx&0-oHCvGok( zU_HW8Ps0uh%;z_4W1~YP5S>-`4!*3b#B^HpHi2N#MqGW1-o@36ca@$F#g4&>YJnN` z!W&lL&^+@kkTulz>_(w2BOynEK1Mf>e5jju&vPIGVYe2L=hg)qT}RIL_XC`VJWJkK zR?SN0Bh6;u&fPv%74mB=yP#m;*SL1$9~sB~@*D1!*WVpmJ}w^JONkfu<nh>({~C-J zK&}t1B|rm#OZVm=?C5_DVn?sv7sMXlbpuVJZUB$>86(z**B)M4;1JCxx}HM)`sLT= z&h$)wz@9$FI6dd1yZ#zH^ZX$)hl?Ge^#{3<#PwrkQ&y)JdA`y!lb~mNT>6A8{Q=MP zR?m~X=a1WV43MR7v)i|8>vAVcAahNUERasu+v5|osZbRYU132prz?<V6-+MHRA^+H zN!FHFTT3*&E#{(Y33JoOWF`>J^-;`*vj)efoF&+IlxweY{DX7#34VOGf$W6>O+JEn zan6?5X8HMVs(T|fQRmOuJAKBUmgpX(RP(04W$=aK_AEr%n2AV&lzO#wCaam$Ge>&S zV4EY>O#T+b6;&T~i$~Ag_pRSF@C;TuKBDUMsUbL^+%Z&KFWV$5k_Go`?ri2K)j()D z;SjYAMCtkn_mXP-?^!}7q;-i6SLc1}D>e2?&-a4~Yxo&;5A*+d1H|nbzD+(?7@?8k zPzJ*g&w%oXmzNVvP|uMOeGUqHLCZM_2FZsGmnbP)toWHB4?oFHVv7=ja1G=08->B^ zRdg%PDQt^uM+cxO?;-+<Z4m**xyY6*O<^ZXLn_gAkP%b~!Le-B16dqgi+Ner;G!3P zbqKZC780;+3pZ5skiAfmUyNHV!o7>uJ4EY584Jkk3o#iBHmDxv)_%^f+r{jQ+#fSX zu{-i2;~XY+`7dsY;T4A#@Y8_Fn?q2#KC<Uo0!+rU1Q_45q=ut0zWdf6)vSx`Z1;Sx zA&U{Ph(L==;21$@y^GO2-hx&Y_5AVL{TlaT>ZM(Z{o|a+Vr%$jnXN6>)p>WWjPh`N zY=(#`ND@_jfhGCzfjB~#89hB=1=$>h>Vu!(9NETAqer!HBrMVZFs7Yb4E!2*4BG(- z)pGKpX$A%V6{P>RAH~7i$)Ixv&1>351jyoMIeHPNYy;>E19on`qWY%q<_OgvdQo&+ zd<Sd!k`$dhLeEjH*Frw5s`Xy3;4^Mig~-Vf&X8Vbw=JyFakPn};;{{j&8Ezbq!OZ3 zG0iJC)1-`Pp4BT9rZm~qUWa?$N?~5|aV=hM)Mp%25I%S~V3j#%_b#cjif9{lif$t{ zB%9joNSaUTvXxjvb=gYFdxXLys@5fkzhtFl?%CR7_$c$jAQW`)qQ)kXP1lCs*t(Of z5hb-DJn4T79?EY#jO>v%1zlbtk}OE7*0N3oY1*1xLB(ZLXC0lD(fCo%Q#wbP&J%Cg zP+e{eqneCe@n=|MXnufXesbWYbH*1~Z3|a>X;bXk-MeDW=4ZtSIL2|jC+IAsvvh$j zjO5TO`(lpjREvVD@j+({&2vx0=>I2JD7?QEJQeE~)2gk#gP#4$b@}eN11AJ3-6~$D z+9Vlum~#uAcH%*7)9mS%Zx9uYg<@Q?I2Y?jF8e&jeh}qq3nE%~$&RCiJ0=z7vjApi zr`K^`pefibcFv-JIG>1)584s$t@DHt4U`2o`2SpGjwUFu>BzchD<Ps8+8Oj3<C+I} z!j;^QEdh+zXTiN*sHENA`7NoT^90+ucY}K|hK*DLYbWZOqo-tbYqvlzMW&drhKQfU z-^a%;4`|x7hsmgGqIz;DA4A(rlublW$AW93voTsZH1T>f(dUKVWvdapfOdUG6+^qE zqL!+lU5cxHJ@Xl9J@eyU=#yrM^PZ8pTccqvZ*UcgU5}it%}0I9zs|fEvHXC2cQh2- z(-8<Vht?ktKK}w+(88(CAZ`IISu~6A-uveYx>fLAkTm?TNFRn~Q->Yid4F@AMW9MC zHgx?suxtyucO13tm}F&W=S<4UP-QJ_SvkQ2=7DF+222Gc-XP0Wa{QGsH<B4FSEmN? zk?lkEI4%ee2Yg4iGWa~Sh#_Y01G@(fy>fI=yC#5Cq~SuY+MsX8QIzsC6k1EaY%lv+ z7_oA;XUC+ne{b+TTU9T%g#gAxS?RFOTN`4!h?56?i*@g+C|MMr{Y$pVny)i$3*_rk zL^!V#<{tE}6XdSmU|Fg%enfm&IfZW^(oU1>?*yNeW0ggTUxQ_vX=;VCOs|Yjh=(C7 zw49htLjC>!+f9}T$`yBaz1IGdTmJ%E$!r=@w2M`}%DVE-sp8lWBX_3NXxg!DII=k% zNaO2Hv~n$<h}<D5>tJ+?L)3XN-o1E>{=GiDC+k%NKl~Qsd$xwmqfoo3zmhqWjQ3#c zP-iZz;p_BU`cT;s?%Pj9r~|Lu1|qnfKNuaZuLc2TZiVE9<+rJr?Dbe`HI)d9;;`>Y zx9^HBYmZMqwxSK2iz<zN30PvNtx-q0<vrx!1h~<*aoF;gALTa1RZ7)0HF51n!!XEf z3s=A}11;|(VugG03Rh|nVezTwsQ{9GJ4B~q@Ywe(nD&Fk)biWB8j1<w#2|wI13wuD zk?b^NN}RI3S2i8AbKi8~!~jPpn`B4nNfN>~*E+K2$J;B@%T`8L&tI`XGB?*#yR5wx z)&2}5MEd_+1Es$<)b)~@(D}+b>{0<YKO3RW6!H63=x^TTFD-3cW0Gi-W}kW&-|#nN zn`f6dch*FA)<y5;YczR_CN2Nwd`<NE&~4@-^YWjroQ}S7JNg!nrSh}}ni9~hLfExA z7u4{xE6FCyx?lxT2Y_Wv>#T|Itc%~xo4bNg6V}9^CpWFQ8QOU%{vzLbnxr})G>WfA z0e;Aqs|8q{F``k&xIMGyNLOtp$9ay>2(Q!I`}l>8$6*qxUxYOY>sDY62FcAxi_(JO zH;67>aTBCZ>g}@fCiu&@6B&*)psE*b#S&jKZyg>K&LjI;)4Vmf3Jr(8;bToErs}ix zNh0eI9K>KBYJEZ=YSdQ}BZlI+s<l)lP97AW6F+$nk(Oa1VBl7LGRQClk>>$=)iL#6 z>N92AAa|jdtj@*;QSu<1y}|W~jDxI@p;618cTijDz=sNBBq|GAIxr)>(l~cVb=^*D zz5#D+g%T3L;aW+dxPrqb9;vG<Gsn{FMAliccxNjVyG^urP%IyFWUu&og`f_A`NY4{ zFBs=uufBc5|A*9!Kcx8zC8*2%OBf%amTUiIjS(-AmJUow-(*zJe{NTGM*c)JWy{Yc zt+CYg8L_-NW0sZ|m*9J$C7uz}r2D+iJ4kzEFSFN7ldj*>pKz^-1MeFt>o02>l$eLI z?+RskSLOp3cvZ%%#1Q-?CK_>HA}JuC_LI_qqXEgj24~tpKw{q(?_4wx#n8A}OsU^V z-(|$hbk8!FVR;t~bWWKbLJ`j(s6uGmijzc30pFK-P08gQ9M6@lvR6uh4ZD;w$aYm= zkWlOHidFk%26fY>cyEm8>>hq#=|Cwj$|w{Q1*AH&^$xaTkx(f4%n=|Wzd51edXT!R zyj1~ZM*1Y9$vh18@LcKOskRu=^%OHd5UXZJQDp1YU`A1dxd&Ii^$M;E^G~?SvHoS4 zc|h+p4`{uF-!bX?jBc~C^#}ZpPCsh&nxk633tmS!KGZjXLF$tX^YGTw;XavS7<B7; z0tex4@8fZWG2%TF69*>q14;)-5nCN#gD0tH0&MItKj+{8P{i7b7FXOt+e8_kQM8yB zK@Umh!V(6bYm-E^fy9BjME&T*2a-!CG8u)1d`v0uCdw$nCQSVRlo!-!rJ2S_N^17# z_>7WNQcqz4&mf*chRF^pwVE7Z-A;jk^Cisnol(aB!KNiU&pwF-QQ0A8-dk%XYR#Bf zPrjYFM|>-2o9L;)lbL@K1K~5DLSUwhnNQ+*#>D*uR}=S%uLsi`#t{5W-0Pm$L=Zc3 zHbLyfkI3jubQ8qRd>o7=W8#MxWdFnuNO%>}&%^;EOGhMYQW4`|6mq9Q(dGp+vN>^@ z8E>6U@vqrp7^g{y%d`lgeDI2)97!Tu+s*4W-!^t~4g2l;6xKM(uAtUOidugTB3-5V zO3=2QYP}q^IVi9!o4pK4X$&$fu^JiL<yYg_o}b(ritq6YVu}yt*>96XKc5zP8A_A7 z^N_s!1XorCf&?|L_i-BsjI4;H3_R)M_K%%~jJr^@^N`Fi?1#<F-YN972-6=JfH_sV zaUUaIMDZh=BQPS4FSwl>bOOxio?1Ub=w9GsbPq}^lK7;Vn6;89X!Q)CvqweE5y;09 ze*N#z>+aBf1VQejj3Anlul1)xuYc?>ek^o<DSU5f3Eh)qEJFE%p?ebSf!tf%p?ebK zL3m#ux+mFK9A9!fM~ul#eTqNbifjahocQzLiGOifrc)6o2XNn>68*5DfS-mw9t~y` zvYJ;i$1=V@(EJ3@h2txHD$lHA#3Lwvw^>9vPU6AScwp8c$Rfl>lZiGJN=${IUdJiC z8<?4zrXlg9_=oh$5CJ#|45#r<l9@UfAtN!SXSj_UBKV8qpMs9B@j{<jX9P|k3Pgmy z_<ImX4UV(G_XTu@g>A=HR{UMC@f_E*;VaD$^S43AH~37KnR=9OL+Im{e{)ARH1jp$ zuY;&2%_S46(QiS+C(d&xH#qtI<}V3-mXioJh%gms!gSG$8-)qf63?sLE%9Cl6<A6g zx`dsZdb54=N7d`Kb0GxHpmq#F42JmUo~VZ3Nh}pO9rV&nT5h6d;H<a1d^DqZG;ye0 zt#f(@5PkfoAFn?xa?7ZywK%V<8qfFoNK`RFN6n8vYO}w=*SYOi+(sG>(M}^G*Nd!z zcWKd5u^^#ChC2Zndnn}{3K=`1>V3&#>6B)`yx6jpb8V*%)#0!Zcl!Ahc!@KHJ+Vzx zwPxo8B$U>?@2coq|1Aee#2Qn8Mb#fuSXOrU{Q1MyKbMo+KU?c*{m)+!p$hX9yrXa+ zC9%W|3PkNS#a=Je7?^l{v>=3%Rg?Vf)eXF!q35k&4SScyUbpIW4hm(4UfGjzU3ZQ* ziLVArOa7(9bi5KQ&1ly!n4+W3ahbLjEvD|Zpw3AWRgm?e9KnDZ{hWahRC9>()IpnL zaB7$R<;R%)*$rbn2dmv`oWgb<Os~!z<H@bAt{&s5skRNFOtIKNrdOAZ@l;k@2UFE= ze_RdmD>of}M>w$ExnVz17AZ!9k%ij$4+neMHQZF`tg%{+{5afmUH_zNXV0no5kK|+ zq^jzCO0i8TNgOIp93rgRi6d`1ai7{#Sbz{9?vCC$J9GuKMgjwkv{Ch<k&=eQ6$rR+ z6&tS(xw8Q3Mi|95vmG3MzwflPQ>xc~Z5E|D`6}0ON_7Bvs&UxG8@vTl*D>7R!d<<@ zr<6eVP#}f7L`QGujunBUluCXxWMiPE5o-?4(fWfZ&d;e!1EiJ%l<Ky^rlk17(v%3R zl19vJ>(RcVn>bvxDe2Ob8*`jOLsR#-nkZF1oF`+z_jtqyb1M}ca#KqjB3)d-4WFTu zA;S<~0+q}cVg%}ew^WtoV>B;D@*b~pluxPnp_X0S+f$J4E65<uRH(<Qlv@i%xxPaM z8Kl>vQQLpeYp*f`H*2>A4pFtY!<FgrdmI5~J}g_ZYz1S`k*ZYEHxgs8=+ZR}Lna?& zZVi)R-;N25&RX_RwfR;hRhkUjGV*|YNm-hx_BG@yoi{(z_GJh^G_7G^;V0tRlDl8; zqV}u-b`pm~mZpf+xla=DD3QeR#)r??PQ#KLUivN3Q}+a16ha7~#(o5y3?ayQf^i;u z!+GpQ%U1+Rya^|-h?p@#M8b_F4iFc8`K!=_jL;qgunS{^*i;3SzMiU0ol|!gYW<%6 z{$kDuYhX-tqAnsaLsDvz2uZB4jSJOdB)Xp0L-K4nHR?MK(;@&D9Lt|Lgzboc=Y9@# zM~6tdextGnDJWZFklMi8675<^sWwB}Ck`gAP1M<OmTF2INdcT3D$pHtf!zXp@QEKG zo3{l);BK1PSu@B<D}6NE6vtWCQP`Rw^7ctho2x2x%Gf_Izb639f<_Z+HGVMy%2^Af z0Si|WLlugsr7T7K@mk`%Eo#ehf=nO?7;_g=PC5?fE;d?i%ca`IRsV$D0M{Y0tfAWL z%t=&<k}ss=meT5_Mi$BF32e*z=Irs=T|Og;oOk}0^d2K--1#5U{YEa(93*`k30jjt zPyH^Y0&pV)_j(d1_~e~_HKkg}T9uNdP1&KIx()=BHjhDkJ7fs2YW#tt24+JLLn!Y8 z8MVzZx6UKD**FWNPW$%2da7zo<98e%lR;AK+>M=CFkCc^(Fuel;;UfFQ5qNH96(zK zP&;E4v*N^6<5H8wKrb0uLc^e7<8ToSyJ$EVl%ezxr;_$5LATb9n@M9uL?q0Mla8B! zgmdJhwYH5DuA2&IQvqWt5WX6h5x)*5emB>St``w%s>J;5jUVCBc}d6_(nk4r9M0I{ z=U!7u5vQk_hB3xSd3#-LEUXW1;3q~B#1D}vFY7s#PuxC+m!~gWp$f37k6B?hRasu5 zT9C6wNgvt5uKg0;!6gC&KpQ5vekVwtA=4WDu<QuJEl0`VX06lm2I!OYEbvzf+n#v) zwPtY#B0uWu;55uxJ_#nq(L5X0&lgi^4y3zKxC^L&i3`Epo=o~o>Od?sRa_7is}A)Y z8FOK69b}v(l<HD%2!_CWAs7&z@I8>LNT&q3dc*y{Td!q8xZbLxq)udzs~U%rq1B13 zEBC<Q8b`{S?Aw95QNu;;W3|IE6bPAz?pQ1ss0^T^eGw3sKN%8wrdppFqbtPS%xPSc z?JJn=k$#sTz%gyYa4wpiE4~64>5;^yvY4vPP*_#F#EH5wi5Y_uP4S6^Q-!X=!Y|u% zP5+Z{f*-Q?(?3JzTIjcBK4m9HNA)yx&4xfcm)7`Di<86Rj~QX(M34YnM}nXgjKmex z;had>Av#Wk1sbUURr5mw-w2h5_F38soD~``!4$YuI?%c8=VoTkA)J`bIdmirmN~pa zpBg$B$JsAz)kMY%Fd$GLPXDL@zVeGL(Hy2Qcttoq?tcWrCi_Sh+S~OPIpmLq-J$i= zuSKt*HmIJ8ZX?_sTICn3eNKjG!a_b5aLPG<VbuA$26&hbfXyR69&cawg<jgpV|@%% zd|_)N^}%p3b`!N@3u7|Djsl?1IfUNz>)ExmbK3=Bw9W(**Nh~yA0s!E3qOSp{qtbP zwu>f`!<iT|<3WcI-uqhS$6ae3@IG1`kaNE}7P-OB<jAps!u%jBqp})@FEvJz)hb?7 z<@z#MbsK(rq_``{wvSUMRN!Pem*C?Xq>8;O|HU(=Z5LENIWb!q3lL8Hf9$<`SX0N= zKR(&X-rP2t03rz15D0=|i+~DR+XN8>wE_xWTOmLIMMb<|d+gf~6;bh=5PJbH=RjI2 zrP{{WdP93cs(8Z|=}oP+NTpR<TcqBL=KGl)u+($T`@YZf{XW0v_eXh{$=vqLnl-mI zGi$A}Z=21#K?sm9T~X^|q+FwKcsTjhV`pZmsu}CXNg*M6#nLbnB)}(jMMJu&VWyiR zf+0<wJB+t14J!Y##5X}5wl_f^fFm8aZej^&`t4$V=`a_=;MGDzx~&RCgMH_a?^jEW z4XuXFx)pCanOt5mCFtBL73y?HBEF1wuQ_brxEpfR?ujOv@gXcPDQ{j?pu%F!n>R3S zg;Lv(+EKsF*rWR?{388Qb6&gLNXcNjMg?4^te_Zha5w8^xTP&Gp+|P6P<l9n!$FI{ z2Vkw2`j=VpQgN-^Tq-x^Z2}C4r<?(Eq4?D?2BGoP8O$`lF^&Usg`$Lal?O}O?=#y9 zv%h8VC>u0Lwcli_H%Tf)ybqogFofU~PJja!uy^dZ2cVgTlnlSd_Rn=`#|^EAf@;g9 z*V6a_jEI3}njiu}VZL;YZF$1E_(yig#l-Dz>=#J6UE=*uo|c@iuel}>NC%oO8R_~t zLo05tbce;YN~Eh(g8j+0rirQkiOs%<Ct#^#zp|l^IQtT_UkMMKHCLHOc|EG{1{_lZ zU`1dFABU5r_SpnJv;;p>P-&mJ23|{MaY8`l@WW6Q4oaUs0R#OtqMCW&DC7s!q&R2* z8j+p($n6u$h$0wq3d4kl2kBB}v*U(|Rq1b`4yvtjxQU4v5Am0qnp9_qGOtfvC{|5U z$Bvgku%hf^Cym4{4la4XvNY%v;2`fm9!F(PZFp)IE=RC{^9+g;ziR$zoG*djkW>C3 z(X-snnPsuYe$K`eQZ~-SO0ck(#aUR!5+^VhX~yFyjSo99Vqgb|!b5d=BT$DWB%?%p z<~qx-GfhD~Q0GFeK0BY_yd1H!K^W8P_d+c$*?G6qB7TZeLmJ&kI}Y^E-_KRv_?f-V zrT?<}s*?B48_|B~ckH)dZrrbg#M&nk6ue7>*G$V|_5Gan6gVR*F^(f?Ubne(^gJ)% z8e1Bn^^vq+#2COW%x89B^f^*}9a_(;usEd<ODy*H&UoI_63bd*Ip}cgX~*r7vdg@i z(^ZjQy`g+rT7o^E5Z+Cph5|e5Mp@EknZYR6t-*zw*iaE*R5R`E;Aar}vMUjk)L>m` zxW?*vuHtF$5GSoKk5vlH-^wqA3A@GLxpwZ5VTqP~qtiNhcK3C*4MMviLRVNnye*ju zGun6tcWC?zS7o?mBYjnF*!4)|&E@>mdja48Hpp*wG|BJ`A}-=U2aEz@9lDsarw9-T zv$rsgPD|)J-x(Ms&MDr``ZQ;}Kbw-~EPzSJ!N0_VtM~W#Vv>+?SbXLh3%%^_T;&q) zvwZDX?Esu-9M7mZCDi8l^PK~ouVf6FdhPNLhZt@sDFrvDw~s6O0I$FS`V0K(1IeMo z?*-5uYT|OEJKJ!by($BVsus1mGk7ou9k^v6X-O8L_qvr57ga%XdGj|?U6Hh);AY1~ z8RhxKq7_O9Q-l_;CLMMPN#+L*QJ#j1M`7!$9MRpWBDBSaop{isQhs2~(`4H<JnOAz z2rBSKg{K3Y+6=!`yn&fkcbkr12cm$7;lHp}y$lo8hRBLQ6ojkH(n$Q)dg?XB?R_3} z2kL7*m3<!6(ipIe)Ou#S)7kFdey@M@y}sS`K?VH;nc{ob2eq^W6ZK@fbbrsYY9MSy zF~Bg&wuJ0g)OsX+X(jHeaO-ZTd^e=GWEE#~4?sZ(ZhZ~=;7SCXZCS;&9_^9`cd4f{ zOJD1ulsxcD)vm3tz!fPmX00uyF|{6ge9t`K{m#;0TTscB2y`Sk+p_eqTe8;F;yG69 z(A7#JOyKCl$-l7MB@V1qiZ-N{Drr9b^qRHp54w0Sz6cY=I8r+Tp|mdjYP!0P(<Bx} zN%|e@F}FS~VnQ#1FIUq`!*QgPrljBN+wXPxX|*LE^KKPFo+6`WP18$=1%l#*TWMS= zAE(J}G-KtRuCvtB0*v=l-tKaYQ(@0^nLcavI;LH~3P`(w)Mpk~&<~86^y^={9580$ z>+H@hzqp)NTS3CKLRBJO8|D+U@6sI6hP;GGRPyk0r|+<L)E>DJCP5xp{@m6;j<I~d zI3<LxfreCRO75l}7_cXp7-<oUaP#VDyO6*wrM8L0GEtNe9U4;MX__h8C8_j*zI>E< zkIsC%OT;P@PSSDFPS?p|($K*w)Y6z_NUXU+-biGE#G0#$-ZkM2WZ#oigcH)w{!^_d zt3vqB20RSFK_*93A|mJb{mI~@Q3?DUr@=)h(4-28d~`q@Pjjl3<}yqr2<0Fb9-5U* zBJ#Y%x<Lje{0-XDk-5Q@t~#x}SXj6;xoaI+Iq(7ed;+L<1GP4pz-4NydWe-@iA&dk zJX1BSn`Z*2o2QU2!_fp<3r8I3YFg}v4jR2hdqZ7>lh2wn_MHdAp;?Ooakq0oQ&iIk zTx*ownjM8>0tu=4)Q&rvxKXcSqtK4G#~%$(+BQJmQF~7bdQP<SF23n02*qPx*n*OV zVQRt|D?Y~+yik<#r2P;MU-0Ir@B#D>*2~+E0}!{=Z6kQhjfs1Bf8`laj59Xh7|YVB zUYxiG1T)|MDL%=z^#TNA0{WjJ{)4W@yP?Zr`rVMqhv-v9HNPNn%}@3Vcsxvvt1w?< zDwx^9at=x>a>C|c75!-2ppS`D-mg+SFcr6p>DVkw!d-eCQC_t-&36#rtA9{Py4NCF z@9m7G3aTsRD{3VcyeCmfY1|&3K{Xi_rQrLAPq+G|u6WHSe6>2<5QO=3gWfMv<4njK z)dsCxGm+*-%O0@mPWcV?FjwJ{zHfERtmuTMh>#c@sU8m<yiF3~3&K@llMnU`+%~<_ zL(=h?x}ziL{O2w1!YIC0{e%0bxy+1_Cv(Oe!ytti+N|z(w2N=BrOq!iwG%6%hq>;k z6lkuPnLBf$eFkrqgn)Si*|rY$?O%4&<{`1}Q6KyOD;#u2ADbg`D@uFg!QcVF4f2~w z2L#}(?qnT;C2*e>A7-fk20bakSP{y~oteft&03O>5tCdSQZW&OCnUO3R0R>`dbYx+ za^m1h7x{T>+7vd#Co~!YOnjdZk^fPDPGIfQG)Pk#{41H6tcD|{&g(riDUzmCyo{HE z{ZVv=f83}b(G-wZ%QPwMinm<!EVO^?V|hOXj@VAfaYY0im0YFgihw28IX6vSuq@5n zt-O;hcvD?h2{uA`RnU%e49+_IPf(_^SxXAlQh&YwREX7p7p%cc(cnW_tcDc`<FWwq zsLsX)|Cv#xLD6wBBHbQq+9WR8D<*EnW|yS9*0gU~c%mFERAy9$+2j;WtK;II2yQc@ zP@MzfnJh}Kb$!RvI8D-&4GExWbi20_^(jJqoK(7H`f0R2Mp_@^INlFWmPdRnFKM?& z9F;@A25=3Hcen9i0NTrR^7@W3_h@s__9*|A4S;Z=WcUrXxRZ?*Crk?!mtI-#xju6J zI54LM_M{8A!9L;*?<ySL3BfS^$Ex+;)qG|TxXH%aaL<d4pN$cLb!jLhPkzA%wao6< z*b3KuzLM;F_F5_mKifgq)2}fC|M(&14dFxD7qDhmi*fP{@h6CkRadj7ykZBVCnUXu zJOXZEPrS{@eBQLOX;!U8D|E9urLhb9eEK~&gm5pcuYPjffbqQ8SpC!+1A^ze?b|ZE z#jDHa!F{z2FX7+?mG)Mpgt>H^ZTrR^@xm(Qdk-4Lq(TxLIzHGpwuUg-3=@Onm70Y= z4D7fo*&p=NQ`oymm{sS?@6ZcweJDL;uPT+As>Aeo#n~IWcuyRVm-)3n6!C#29rhSW zHgw7js1Y&kzlrdQUwId9u>`#HF1zpIkV{|1t*CW3oapeYG&h+|lV?J}9*p-vBzp)! z)|c+EH}0{k9<(f83t!8klJ0pX;69uD8(S=J9q2<UV!seUAWeU~%RAQt!e7P#BFg|` zN#u=*eGC<9GY%IL-HnNTl^aO=08qQ2&FNZ|(Zx30V_O>6w0&teh8a7*v>P@=>7Sz; znYOT3^<UasTcg@0mN>kIE$Y?{sjb&Wv`t;>cOPMXTbqBZvL39m47K8&BD49{f#>v0 ziNU;V-~MlePc|dN*4A5Xiz!Xg61D-QG&kRBSri1XEo^<umv*c5rfp$h%@aiFPBdTI z@#aP6_@d77D>}z7i_nJh-kNNG&G_Xta`3oxnX(6E59rc^FD_T!?%QRABQHSd+Z7SZ zTRyi(Oe!-iP$<huUP_E6rm&6iz?Dk+&vIO;w8eT@k4n6EN-U>6svpn;87B$Xm(=v5 zSt|Q=bt0$5@)|Ek-T_Llu2nxG)0jdVvYlLJ%2!kiZ4K!=01nPJ<NV@?6x`zC4x6YS zvJokwi0LF^x+uH+LD~H5iz4N>qL`@ec3N_moy_szBvhcCO=01yI|@q82^A?5qf#c0 z(9C=yB^UIDEG5??CD$`0*DEEr->lk*iDAN8;TQ)2_k@n7Si{$4XLSJr2*>emhWFV! zFa87{KhUP24JJtA2`#5s)7l|%`uSI+&*-LohCA{IL>|g?ArClz&!ehErmuZA3=g|> z?y$CAmE&>sZlMEVQ)>&P`RW7L=xWru*CaEpKzOZYjzFkkobi7CuH}yWLuoq7ypcy8 zHh3?qt)ELK{6s&P*$vC8&Sb(B+u5)o4ycTQltQh-I+;lwv4N{$>*AzIaq?AbAu|!1 zNdpv_Yw1LU^56lr#*YrT!|oR04n2mF&w7OpHj4Ilr3YCWT}_7_dm)6OPwi$n^?i2L ztq2VTD?&p@Qi_Il>j=#{n`8|?&^wH~gtr{%Zg!K{d{Ty2yDl7TG7hsM!0->&@Yl`y zh1N}C_>5ty-^22$J7VDxYUjrZ?xTWxJnYVeHfra`>LqI+T(Dz*HN0meT=hLRLL($w zASBZW$ri*SHf8==*tQ`QG}@l?sRMyRUn&-GtO%h=aA=}aNFj!>S9A=V*Bto@oY!J9 zs*q7C$>2h!eK?r`dor0&$ke2geTW@M?!Y~W?0`E!AV7diWfrOKD@N<_ehpnL?{2$b zZ^<8G$q%Jvgwm3P))Y`ragX_C_)-bqZx%%brX93F@I)%SV^q+wL5UrKyWWBWW0PlB z#%;FjC1vyj)0g(x*{k>syIE*{Xb*m15f?1sIE;VO^Q&xM*;6JLCo|m6q5Oc|PfC!D z`8*n6_C*^=TX=wng{n4j0mb1*t^RbgUvrkWEI(+~?O35nV}&Nw2u&KRS%<`$D!ccS z+?nn+y)H2yq-}a?c710UZPN}r+i`t!TfX%=Ew6JZEvP$e0d>db(>9G=K-)An|ES;| z4_lK@8%whwR_}mEXj5Zt-^suEU%cVOwJ9hcClyH5P=`%HF(?d7>M#l8ls+US0PX0q zj^3Jm*sh#FaPYMI9(&1|<A>?LvTt|ROw?&6>NOKrw=5b&zaL6-p^9x6^coAr_gVC) z-B{s<&}jOK!ThzQGqoWuHLr`#xB%fN+=c#wNHlcy4dzk9@&ojFYn2rs&uH7mzC|1Z zQV~cqQc;=eo^pB?J6fxVP#pB>BBkNsx4eIKUBb{aA~qa~$W}t93(tb)*D56Zh!Tca zY!CR^5JNG~rX$#_kvU3!NgEp+ivg~(N}RHWH5Jx?2D!t=O9<A(q?!(B!@)x)*<(eH zLNUJu)3HI(sEr+*<qbDKRUbSazimQ{AOIXYD$D0WigGN?ir<JhlMqL-t&I&It`=UX zNy~)FBYJ<Z#Ibk}7V0y&gm)3@bJP2~eqag2+dsQqxdWnbtHe9Z3w7c+yz1>C;k`LW z-GVuWwAdctIiTh|p=K$vms0gg+BISauc0;Tkf@;4ZIM%&2V3mM>77N|5WfaDt{N<5 zZYEc6%Vbem-C^D`bX?PzT9NWDfm~Vx&)N8Ee1g{hDc~Xf?QT4@PZ#kCXWY?@Ef?*! zDc1nPAEJjKnqp+2O(>sYDcd$U%L`$h%A>H`gm44;WVUTo7CjQ+H#lIWLp$4+lchk| zWd2f;xJ7A&lWym;sly_CNspnjZT+)ix-|Z-*<0-p|Kj^r1HzJy-F-Ny^ZeRwTo7DU zYObufcbpOz3H&g8+#8%n=-+eg_c<KooqB;-5vW6AoTds?UC;bZ<N7{|Lq7z?=b)Lq zitn()8Sac$+#pPcmN42^1x8cqPz26|Ew8#|4AH}(57EmFtZu(rSNplHRD$=C(xN4x z-+uXx?<@a>Ef;f0gyTMACjCqwIQLE=M1Xhq<r<_M>V?@bcCya2Ag0~$eS-|mbGK}A zwFj4dUzk4WnBn~}16F6y8I)$wYsQ4vs5ncr@3_^b5P(tW4OOh}tXg-R0t4xdv|82x z4lef_0vsLmp%MB;o8rQq7d?6Ba!;<>i-X*(>Ac;NsMF^~VD-NI8&X&l=V4<$!FxS* z(yB&v)m}-?{MIx5xEA*dWmBr1Ib|;xy?TZ<g+dtCdWz2OwN!nBN%rN+eYooWT!S|U z<nn&{z&J~OKcog>OF6hQ={%T{IUcqmP}P%*ijF1VAO>>)ngzkJ0@gJl#j0erN=^G< zL<sFkDT7tPQ6a%zN^KAW^<N0VG!&6*1!tGxb?U9Ek(+X8#>U*@fgP((nIol8#q5AF zU;t-Scx}5E>^mShG$1&}SDWC)J7Wv3@iJ;~-sF@0D|U}Ho@f-M6jp?7I<D13KvIB7 zF=*dGus>vrX`~A*z#GBHQat$J!2Y?WmBtEvdXaU;KGE||vnywptT$BxHMO>POx^v> zQuRvP`?>}{&M_AihSJ(Z8?DFW$fDMy2|ZG3YyDn|ww$qF^t^LYM;0cd8%xBks;tDw z7$ZDLe^%d?cjR0lTrGJ=VcYVoJ@{7-00EQoxkw)*R~(tXZ6ZBM#{T~)94;8Yut(&{ zvsVLXQ;-)QA3S@YCd&)wHNLo<%^oI-$nwFF2NEriD~pNpJ3C^9`T<{hg?!s_dXk_3 zE|}~gqH2GRcR@EY&X<E|_%GyHQ%UgtvId_x`tk5VTuW6t?;7v~cWEGJY_Z$=<eTzB zit@)=@`mz5OnHNwcS?g#j{2&sf3+`XHN<(}b9P(r9qp_Q^~ELc5M-%6-xuLcm8Oqp zm|X8S4X6o3yO{9x=#pRT#wTJf$vN*64-M93*lK}nwmkQ0i=@^TC%G`i6r``w{C3#& zc~`iU+TUyvNj!l#u7yS8&92{ApZ$agVZ2*ezO{8aKd|-i>RsZ_7Q1C}WcCu23{E&7 z#}AdZ#NM6v1_yEgXJkYzM|a+?bS+kJy7dlDz?kINWD=amXx9fHW}}mF>${5{-uvL< zvFr#~K2=rI*L{HZE2|EyKClN}X904=7ct4Z#4YZ_EJvHEj6!2|%h4cA$my7FpHBm0 zi<c+?iEDE42%QLr2*L+#4D{)wee#gXB!tNY9bu43@W)eE(pp#301=c0Iph#7GVMAN zq6WhwPP1c@^~ZH-6}OD;VR#b{9IsQj+o7T<F31uLk8K{J*8S?%vp3y#c@JQ1ryqKk zah<0hq`lH<s`|NAw~Wpz`){BNXf!@1+jP)=8cjcq#!K0Nws2H~saZivzHoqui|WP} zhZEW@xMq7yGWN{=!*+}nL0c!gJCJL}4h>iRQR2qqh}^|yk0P-i;t8V}aNSuvM7vn* zEtOC;<;s24Jzg!0XxndY8@=y38BaaHLoC5thw%q@Yz>C|jm3j$myvebWK?KmcMxa? z#hhm>r&cM6D3l^_I-;tT-0`{=*C(};JKH>%PS>Y?*nYJ$aXL`J4+JVMLdos!0Ipee z^R%SB3!B-0*KF-mkx@+sEahxiw2z}@$|{3>pVr2VYM+TP{reZ7C-Bl;wz)Z~`C>=w zMf19W13SSH{A_;|5X|vmrQ7u{d{lEA9fWtO+|y34kh15}+#GAsGmz*Q4r0yKG)@=$ zndpMPEpre*I_xj>lDeko`?kPA!Osi|e$L+{)>Y|luJd5AWmP2TFfJ2KNa?DiqRuU! z+mU{ZC3BE%iVl4@gES20<mQ`-u#oVoo7&ikw?~iH^KYLC;dWH8Mt;XW-)QtZ)0DR@ zQ+8+vxHK(j<7bvVQS-}Vk}owPVoL7rUJ5Xjxs)+I*CCYt86L_h#Q728g>*Id=bh9; zxQx4OnuZ(8^B!_bhw9C$TG#Y?s3l}d-!(Kxbyd8$wPjM+iFXY@NUomJyRx0G67URA z0p2-Jw7H5~TfN#QjVQ`<)(&KClcI3OR5&ThbSkJl4cXIcXsA#Dwve-SoT)L9xare! zJz_+BZ1^6-oO<0>k)dMvP$*AXhL^da0@Q3?=5~JHVWu&dsAgBG##G5;J8H%0aTK1r zZy8#n(w=fEjGKAltruTyiwiS+B{_Gb)0MRVCcCVoUT%hl*y&H`UB#v&K{}n6J`Rw% zYM*h!QEr?7TU^x6Q}4*iwwvEI(2Mjl?X|MPEc{Cgvpfp3JPWhD3bXp*iTAjfHHoMI zCnCyOLPaJ;CVgh56^ZRgJAHfjP_9%IaF6||6tjAD7#`(X3r9EzCe2!&?n;dcx`C_0 ziGc<*bM~or-v7oh?yRWuTYKTW*ur_^3g^WY&KqAiFTQZz1lv@Db{O4DwFzH^(_KV~ zZECr2iG}`K{Un}Ru~mni8N%TfFCtddt^Wz{Gm80bHTRkQ?AE|-8^JI~RYjfVYVI-f z6^Zft`%OxPh8H@YO7vB&xT@Z=^#jW|yq`-T@TDK;WR~BQgDrU+_-4?rGjNXF@D#^8 z;W1`4y2LxINli>@hL6ULo*6Tl(hP}NC1ci*2oXsUi4-Dc)Q1cgjdRBX0dh@;N^7oo zyX>5)F{ph!dGYPS_?f)V`qz8)0U^n#*UmK#A#Ur_Cb}A%Zbk4qemvO4!XpiT`L$Cm zniCnJCbY08&4M@%3PVXK@4)aGdxoJzM>(Z$m(`-2>}OF9Kj58zQ^<w=!_N%mZiG=f zue>j}%MDv4Jrt3gv$qMGZ0Hec6E@j4Ve>?(XJHF&8dCC(tT5qj;Y6!hxP5^jVSvMR z8k|AG_*ZMidd4|A!W<&o8JH~4KeJO5N-MY^{=wcUWQ&u;It5+-RUk04t$2u$p<o=e zn1t=7itgi7-FYQfh?j&*Vi{Raj@v%V5OSQaQ3P9)6w=@zMjOQx2>B_r$Sgz1j7F>e zB^tS+zhr=F3FnfRK+EYwoA*jdgOH&q><A8i>ZCu>jc?L@d1wBSE0i>86Lz^R!tOX7 zJAizYVe#N6)Vm%qnsr&aIysIt4@=^^?4b6LK4Nej?`(*aHpL}vmScZ}v<KgEtE+ig zq%veR*NGtb2hk6l>L9H#C+;{c!<I81wq>vib~;8$j!=D}&v}3)(w75s-6xDuL~JsV z!4S9eT(EDbHZ&NlcDDB~T-X5iwKI2FvpLFK-_cxeKIX@VZp9pv{e!)oC-kyH>#w*p zoU6V=&vF&d>1fGAiVe*Yr#vbN3^}TM($4wCN>?PN{T%!TLYeK2v`iFu$T!$GK^q!x zdjCR%j48g$hKEOmM|S8U%{x4Y$9^2NOOoA=SIDwmNa^M>=g`gBEOIi+L|R{ILj+y| ztsc%<CQCqODH3%xl;jlpNja91=$0KF0Kw)V+KpCdl9`55=x{?@wG*m4sN1wax8B!r z@q@M-4jL}dD|luo1&~*KJe^N4W`g)@--KJf-5}=tZJ9pUp_mp>dhU`PuXYG+euIcF zHL_>Q#OiP`x$wR=#@VvNPRo>&(g-TdR#|PP#jo=g^kf)xHxE8-478$o4S!SLxr$>= zsosOPF*2R*t=4=|RjQ~tRobeR>MG^wm2$&t^58H=`!u5x+O^|^+?Ht-fM3_bMDcyR zH&O1f2-JMISxnZ;!b_#Yh2An6g{b~73Q_ecq--omS_N%G^l=fCAgJ%KDU35xm{VoZ z5sIL77p7GUrQ%`RA@s|1dSt7@&Kd5+qZUUWr@Bul?24>)XejiD8<E@@KQ1{{JRAp$ zOT_9Z4ny*a4ez*?Mwm~?yldTf+VLwzqx1E_M}VS^yV9oT%v0qMmOmjw|JOG?@Gf;e z_@3%HscNd+t*(8t=(r47Zg<^1f@@wcG7Fmhv|)p8jO2>{Dn2ekZb?nDwm=F7-YCFi z)QSlx(^`C7OheX4ld$*F9=S`$PN$Y`9oiU~G1#hoqCecGKt125>bSaUzvMv5x@)4B z*#e<enz4_hGX(GZL;YVcc&Eytys~?`0OWDJ%Ok*@=&*Hwd+<E~tQG;>asazQgmWof zoE*i(YUB1(04!QMcuf=lm39CHzy<)Et#{SdE0U`z>wfwjfOl}_qQV>FA4wa(b=-B7 zM{$>KFc_JILPJFxQ#|<nrc#@^NLE**8GOWCYQ;86*h3j^QH^dlMsbGcq`&W>j_ccU zj<hh&CnzZ9i?X_lwuMKrFS9rrzFt;WuOCYBXa&j^p!N<L4UIQEn!^^w6gaSE+x;o7 z_=O6xf8598$cflj#1FvJlF@CSi7tvyivb>3hdfo5X=+;2A$ps5UDesotM00+YCktk zQ*GmU|ITmh(7G{ooo!xhyfgZsaYwWHytTO@9XA=r4zRY1&TzDRW8bkAV*a-0V{7$g zqumZEx3uq$!O*aI@HNR;oFVt)=yMdl(VR9WhL;FHV5H)1&+}Q^G!;Ow4Atw!8_Z`v zH{Zn-&gYhC&w4|xoHK=CfN7puKXpT+0y0(pra=FizY3M}KP*uiY2|EQ46R>dU!n~i zMJr#{Q+cI<#vlBk#m&v;=Bm3WqmOaiY;kk3xH(f_J*cOHA8f4WL?Eqg*c~z1@vz!Y z1LV#f&DO82&DH6kns)q?HC=RuW8XFrKfv;h-84sCb}d`oe%RIKsrHLI7S%~<4!0&e zQO^BrN7YMy67J>nOq91XTM@Cd5{PiJ@3+;Oj$7z9oyn%$T!->;JCCia@>!J1yPvaU zHzITwQ#^+2bTqbIw!vN;#og_D#U`l#vtBjT(D?uvc;ScMBPY9Y0?!4{`MU}Cs1OUt z6XnS$!$hb`&}IULi4f&j&b!dLVno2xTv>vJdByu(r-rc{4o#2qXWjAWW7;n6kT1@Q z#)Hi-3)2GbN5<1p+ZI?18{M=mvsihG-i58Ysu#lOac*X@$wOUMS9AQg>Ss8t2AbJw zjYz7E9~ID#i%vEv_1La1zONT^Mo-3=EE!$85h{Qhy@-Q3qdHx3SOSI+U~Y76a?J#& zpwtGBFez7uBp17K>qU6nb-{fb!wqRN^3E0<C*m-+UUsm~h7EqQMAsxcS!pob++y4a z?NuQnOv`@IthlTtHcC5iz!m1g3%i*8GMh}I48?PlqOw8Sc-(F(&D~5Un;=(1_e#6( zv*2Xqeb7JGGg{xiIC`$hy?@vhett5?qEoq>hL&XKID&+B=ELx;vXO}@i*b9$6dSdR z&)#?0!bHU|Wn<7VXV8XdmxjBnf1=PbXcr08F^FOtq=zL`^7?nBwXszAz)S1pZVJoX zpBylV%bt5b9w)CsyeUk(Kl#kF+&PhQ&ixQk8!GoRw0^gta!>eZgZiDtTd5Xz4t?!B zIzJ90QL@SIkjTEy(n$gHJnFFIK6@cg<HVZ=uR%t_xx?T!H80%fy*M}2t)Irj%QSdh z_N?xxb?puSVtCqj@8Xs4?1QKeZuNq^SGY#7oZFh&ksBj}gZg><xEqgP)9YHT;@Y>| zhmu?wcppvuML#9-qic%VEpIvFZ3vr~5ysuglauJ!v&|44t7og#T+JKzPdin|axMO3 zJ{s+2%Upt6Ty2!gSM@l);rT`h**9<;@6vqAibC<_?8wn$H%5Z|07TBv=n%DY7}j4i zU;}ElEuyDMHLgH{(ukl=w9T`Kevg9gYe{7wema{(%q5Li?E3y}(^>vt&BAu0n5nBA zQ)!&RR*&OsCm(Vb?9h{b;Uqu5eL*__w^Wn5rZEn%#T4u~PFZ%pdOSxZUIlsweE{7D z-C(^wHVVjYI$#>QrX~)!<o#(ghPs0Ms2K-;cfZ-iw+CHz^t%LOf-OcLQ>bTT*m9dH zEx|q>V_nKV08_RHue?NXEEBUj#v-i!v^fRZaR@zN`CUg5Bv3IRcQGLRci&?{`?o<` z8Xc|DwguE-6RuG}>Ys_{Yzx-%R3lH@MW`_>IUu5E?9XM{rfhQ@%=9Wp4YBlcfsX3A z@|QcV{FNK2l30ux+{o)04?#akwTMy*6_n}>$NSB${Op?2`<O?fKEwLie?HT`<F^<M zn7j%dbpXA0pDoSgB|A%3{n+bUTs7m{y7o2qaab_?vzmYWmXLXTDL+ttdi9B=S`Sge z(jFXuZQgG#RmGV-o+#8}h*IDUB=D}*#9=~2pFd3{as0s6H4|yt<LN9Z;BJ=)+9>oQ z;&cVl3K*YIm<c^W{Ij9z!=meMesy>d*RTEuzgxEx`(N8)sghLHTXA}|VyjD=E8|wz zMj4in@uYUOFz*6Kp}c3n1bQSA%SOaXB4Rnb?NG{8Dw=91nc71@yee<m1zk|ZkPQSx zxqwGG8sRNfhK;*VqsSIo<S|;L^yC(P5=!J<S8SC)iMn6q#%g|&4cwo@+DDU4k+d_G zw25SGv7#1{M@uYY6L~t`qITpRs^;^Iz(^Ew4|9J!^ZqCQP#+tukFBQ+<?!zMK*~zM z%XVgyczv?>2TnZmyr{6Dln<^{pQ`5lqSdqp{OE}GVJPLW$gfUA!JpxGi+8!5IFKI` zk-@Y8ZKtg(w)K7K$1V5$D&6Yu-)iggO50Sj<>2i(KC|EDXPMVq_a~bzx)l{JHaC23 zON6ii&@-ndgw(WyjbGJbO}pMBC&ZUd*dL=Z1XYUgVBEF?Joh{^MLhaN;T@SW$m{uO zH>PfkD~h^KhZk2X+LkDP+Z?{vd|cFi3AO2FR;EtJcwMreQk&pU^)G2o!TS-?h#(^Z zGI()8q%=KJp1&eJQkn#*^em&$mk>8P_Ia_uN3KzeG-^hp7HiZJjhfY{!CdZ?F_Dud z%L{{xS8R5{fu^5AKOTnz6?EY=YV~`kISJYwP1@B6fH1aspWLFwsXrY<2vJYuG*une zZb;}lE0m1{h)>K+sUk}zatO5yUIW{Fe7U*#midahaPV4itrx~g+Tuhlaf~faTu{2a zz;LSoqU~|4F^<#6IZ=4%8!Y-*043ySsjh+|<_2i0`+@hxtrK$83pY}R3VA`P)R?IY zbD?3GI@p>(dd^dzm1;7nBPFaHb}RTr^UI&7U&e+}ytT$jWAlwuXh3J!W*Pt$)cMrW z680+fLfuY2by)J(_`8`H93n8e9)%1^PY*H_$;_oPYaQ4(aql9<i6zqEnhp;xK}&a~ z)KsObDv}zC<hmkxafY}+D+eyX->J#bQT%1llS(=Z;HXBgX28J?Ib*$=#T&MIwUhFi z9g}ypfIOSP%3_&M(ThE@#YEn?>JE3ykYc{C`MPwUyYO)GX%VDJd-@6GG*`C%o<|Qm z4DOq0HhM=kS7<iKp6_G6Al=79kuZgee*2#xt2vLipOLk#2Rw@a9=&<|Zy}UG@=#Rs z4TTVWV|W4wMM%af`x`!ta{AX`-Q}6GL}mDIifJN~C!E<&!O4K_Dv>kCm`$E8ewAhO zl}{qjWnc%muC$fl@p6m_<P}?GS5SRR+ytX%aw0cjJ6?5iYGdb4*gl1KkEgdypi=#l zc9~Dr)x>>WWw@yjbD)e`#z9A2cerxg*9x%{q6|Tsz0Y;UB5I4JA71sO3fyf~cBS`k zjZm~f>9eo?e)M0Xy?!%IV-!{0^=mQ$Dx8;36QLFqP~dgRBpeS^IBCG`@S<d{8{kBm zVBc}Up&r_pk+{vk`uo#at?$qiPI%b}NBb?+_nOaJz1^MnZRu~q;eZF1vn8g@liBz8 zNZT|9e3>{F1hM}R>oHPyGR}u)@7r=6s!KXOMeF`?*)~mtWPE6)Q_XdI{7ricJNK)D zpCp|(w+(dJ_xGc==Uq(Wxb`3ICD-lRL3=ghz(Ve%ftF@en`wFTN^`xwokOKW5Kii3 zT-!V(v0{SwtFn6mX-{yw`nJnHFAaD{l;b1W<*a<>>k3)g0f~imtra=s$o35xYeR!< zi^q`%1U|~3JUNSh0)#p3x&r-8FRwF;+hvp{=bv76rBevSHlZ`KnES|X#MhM!;HtS~ zFF9hTaETqwaaxl7;C|BQX4CU7+&_hVO28y8gyB8qf!%!2!4Z;R5AaF^By!P=o|)?% zX!pMRuo^AkTL}Sef%d^S6-?QIU|$9j$~AbWH=3&YcJ!(0L|p#cs?uxTM<%qtCUPy} zxxw4VQK7VO+K;mTE;2HlMLgHUK+sHACj-CfG!*56cED--27Bi;NgLzV!no4cur9Vt zgQg4KHqO;DZf<w^>O_vx?ft?>Y>uI%JP~b@$fj^EbRH4?RU&)N;gqTY(jVgf_TR)A z{+qb+$KvEI86FLWP<22OM_FS*MtL+8NYNIOk(Wq^hIF)vt(V15Kd{=R&30+4SnU12 z)75}xM%+ro=gON!;;Yr?b~hw*)=#UB^VaV?&!ljCpPiHU`m=4HNUhded%GkXf3_Z1 z?>!`$^t1JddhcWj6vErod&9&-e|G!%L7(PN&1d;5{trYQ?vE9gw)p<p@+;MMcQ-Sh zt;DlK<as4PG>tnmoof|)HWN8<H{g|`@e{LWE|*47{B*DeQu&t|1TrjDo}}6{TuuUh zvu|sOW+r;fRGd;w*yEndPD;%aPl}unc@tcxX^{`p*oOhX+T|>LT1k{4mZdrn&P!!! z-7dn*qzRs6B26)wk$>l@p24YFJb5yFCKoQ8H4}!$$!2jGv$-X6K=~JWN0xH?y>;(e z2hZm!ohOx=PIir&?^5Yp>7zgHY&v-t(#b2fxKe**NM*GCxRgHKbBcFS&*hpW1Z@$% zTP*)RBSsV|LvsX7=a7GRtXvgqpskXDHxn9O;F`blL_2hJx5GWOLx)&=mcJM9Ky<|) z*nA9m#{)$3DDtd5$ZOVMPNY36wD^B0(JB@<r;{`3+}+|60al^yPn26}+n?CoOdI~X zsLa2Ef4syi0agt`TcJ=C-P2-(g!GGM(a~tr(R%Inj82`ooP^UgA-phLo{=6tJpFbc z6CXs<lhvb?f7t|nOr88zS_<BzlR~|-E@U}%AEjeVSGqhP17dU==xZ69#6waY31fQ_ zHpRvsq3MtaNrhBH;*#R5RQY0LwW5lwc-4K>NJ+TX>Zj;X$iVqBY9ynuiR4M?1<E%E zc8nScMbfKo76idfFLuf@)8**}!FwD&VGenjkq2~x%mt85V6M`o-%3mC0XBCzb@u?9 zApoldulpwP{8S`t8tE^gbrO;}U_}}VD@`;%Zo-FwkHB5=s`ID_CR|~qU<!^e2NStG z9TUeNy}}g~#V62MA}0%(mx00p`K_i1f0h)<3LYU4?v)M%zC;oDG7#uUCV7;M!c;`@ zhm=Ahd7N&-he3~k`p-C|I2tulP-{iXa9c&80!(`YSG)`)6`=0%v_S#GBOD3v2snC) zR{v$-h^C`B>PhB+2WcogJvj1z1kS(W2#qM<s;9|F&QWL8QklF+?%f|FU@S`|<&##( z3R1&MX3}{CFlK1Dq^04n7d^}w*PJu{>+oeCCaw8!-Kdp0?C|tdc~Yt@X^vXA!+QJV zGsIP8tb)dqV%7NKq@?mh#q7ul7O1!gf9u1d0V_5+HHHuslFwD=h$VidT&LZUf{FMT zs|q2v&)-E3<b!I}YsE>ba>(k{|5jBgsC*-;NLl#PYQG{*Oh}Hn1EhTtLdK{IYEkeo z`kAp>9#|Y6j#85%bv?zd+cNyicR(<EdvG0<GVd7S9Od?TZ~B3yTE4kBTn=qb-dnp+ z4phQ8>c5L2YLz21ztZMX(T2#Jajs7DSwF=#(>9P>JQE)Ep*2#}fvy@UAJ`!eP-D1I z0ri0$IY(Lb60wQ~6N<+X;SSvc-8g-(`<$T{PMdPZe-XZ{CTUI0jL20pJk>dD;vdmE z3Bd9$kFbizB~Ux4qET24hV-&oZqwu=R=X*}->NAZ@Jh*WwAqA+sKHVDpne2>tOws8 z_>~I0`?t?udkn{4-=}q_y0CJ@Nq@v^vZG{Uu>%=Ndu8U-aCwjwDvSqz01zH4f8CY@ z2eS1p6xr`k$!ABq<*@Jh72(|i+QvS;ai26&=6#9+0I`)n;ESkM%e-^(kOI${3vM-y z^5;ms%)D}W(*#YUGtLZ`WN_yF%MTV6IuTu&`X%j-x0m^8iML8#T5M1h<E98rlpbkC zD{Y9R+R=Kq5gxYyo8%v|O`AdfC0`7v#(p_N{P#A#jf@R?yf9VGyY$ZYV>?;s8h?kj zX$N}aaJBqRxp$)M(3|ui!fL+NG{)Z*`sb|sm#58v;EFR2ICp1o*5k_si8bEmV4dmY zEvStJ<BL49m?)}CrV<=?{4c)E@BQuT8*rsw4TXCuLLehZ^BGGzFwnrrRwMTw?R@Ap z=(qbLKuweW6&~M5@Kjn=sl_C&Iz9~W;4<!itV@3HZz(*}t9ePIKOw55a+=QoQt&J* zjl`}d{#nxWdB<8=Q>kN|;IR-)LT;+HGF7R(EQF50f>&6e6Js!ZRB=fpA!+)D9jj$& zArF@<5f_Y*H$F+{rHt-*DQDdKIpZ&cuc=9TwZ?np58kur+<*a8oylz}t%{UZrAk$M z^hp{$?`qcS_U*+nhst6SoY1EuQd#ugbFlFeK^iS-kDhlqShw#W-mOvXF`4Uh`wlY3 z6QU%AsY;>SVG-PsCjNshk>Rt{O-jEw$OL<-&~)kOa@y+HH|?rYYe$@H!v3LOmSEP| zX6w+(%D%VKDFl_07KsXepD#dj`9;Q4#gngB5Wh9nXdnlCgF0RMc8)l!ZXb6en(iqL zw|}?K`V&*NJkv2@VZiXt-j(-wUw|Ed8F}=ex8w(Vucl9QK2)<EO>gM#i+I5FQD3a2 zebG>{d<%V-i|^fjj<zpJCS*I>{(t=T-?lv$+tc<X&vp-vaE06fB^n4ci=+AJ9Mx;= zI3Id*6UL6N8(vJXkKiq(@KRbpfVX%5cZ{9*U_uFw|M}a0J9Zkqd&W+@*CS-oIlQ~s z!h22Zj=t)>@_}Qyz<fIzcU8UC%A9e>a>k$S?vwYv_db3D+`H(Is5p8gIwv}%bgF99 z2-i{l8XVBOmG+v~9YOTX4`Wr-iuHrcb;YE<dPbylhUYVW^+H0dnRcuIj+DkXAL3#4 zbCNgqkC_qaJi{}<a!Ta=7w2JawC9cP)$_16SPo;|rE5g%1mjnJ_1<ordAGWwUe@@l zo%X}^LO+E1MD#<U7rK)L)#ii6RaNCB64deb*%4%GwRwJVl^Ku87!>G-@<@4Eq|g(; z+<VurzU)VI`ZVvmj%>Ma>^KiBxnS?<puj0;iU~&d8JqiK70emuED&f^q}D-*fImHH zsl20z5+z_~_blky!wRYU<Q!G0mdjK{@<T<!`sN^BiUei%kJ=z-9MbtX(v{u$J#TNM zLqU|B`AYQqXJkesJ7cqSaFn|M!>AE=M!m*Nu-u?&UVro9BQQ`J?|~!5z}0}hr=5i| z9>GFi8tx{fiXJ+i9qy)`9)pFh$-QBI43-HNcj0RXEQ6zNP{_VqZT|x-%&K1<Sos|m zE@!;77e4L?^>iS>dxqfoJ?)V2xSI6ouYpYb{>YHmqr~Y;<<jvg`HW(}xZoq9n9)GH zC4sV6g0feQlgZ=cWkD3Eq<Gn?TjAq{*A8e|ua6U+VIoPvAbuQp@iyXNQ%a+ujlEkW z@>ihUBYh#mOVX2WrKJ+O717GlXD+91(9&92at4>4yL^6X3QI6O&W@vdk(6q)Qj|1C zo$}T|RjSpRDw-WB%$q#u=8;&1MF|13hO4S>$_+PF1-Insw_;?`vVs!5DpGGK(pOdL ztF+)&zg1WCQd*MvR?df#>SS?UY3}fY`wt|`5hcGlKQbx(=IAjoO90J$MW{M7gH~Xq zTJ*A*zOI@Ouu#Hp;N361E8d;Oh4nqn&Kmwa<L%%7*dEtm?&s8F-D8sR!EJw++g<Lv z+|gqRSYwZUB=X+?;>KD~{?cDSm%Q|n_L%3Wr`ZYMFuMWiYThGMEqoW|Qc!8FY=AH{ zQC1Ttl`6eFysG@2Y8p^hjU&;#-RbGK4s$h8_QTvemr5IuCdp7vb)-jDZ*r}z)@S&I z$H*fx+#$wNGseEM!4lv{&dX-*ZvIT3WW9OfhPg>U*8SLF&+{YPe!O~VMx^VpJx-%X zbe?+HnbGGkXN|OmzbS8j61kV2F=Y(g?rO~N-zC=RZt63nH6!hofPrT(k^U-MfdAba z5dl)Nfzr9x#a6o>`oiz*>Em{2bo-2FobG%ixz=uJdf4%am0XwUns2uLC~r02)ah=u z8gA*{(KlD>o4?lU>h;aKTeuP2P=D}%S$FG=ddZDs@%_3Db>@zn1U!!ybvvGRvu*(I z@8GW@nE36)?MVp6C5NhMn@osg;zy9%UOd0y_RT)K+~X;(f$Rz0$W|H3w`_%Y@s_%( ze^`T0_NO?^swS(e(GW=m<+>ud1I@df(!+1zMsW~yhk;W&!&`2+Raw5vsb;V}z2GLf zCOcbh3Gl`VocWl33Q*U_ZO;5DIZ;l>K9HoGdYBl0SZs~6s@{@k``Mvz^03&H;q}0^ zGs9<>1f1fw053~`4_d(dgTA>%{sYwVE+THH=I_cm$TTh>Ka6JL=MyqQra1Kwiy-cH zP(4&GEly;r5~)6rE4}T!J9--WTUg(PuD88iP}|#XyWC4esA^7kRnwcx&wmY(_w(nQ zKecL4_e)pZN>93(zSN5MGcpE@?bA4vqK5KGX{(`~1JQ>iv|m9Jpo${Iy$2!iDy!T3 z*b9IW$vaDE^UG>8l>P0<4wbM3$k30>M};orF@G$&(2|@;mt%Xl(Ie4??kzbiK@Zv` zZg;gq3kvT0GdwrB>N7kL;$c6&q$R+UjFz<pc!ZCVx84H30PQf}%-@qU#_3MIq~()` z2lsaT#Y&!*g^!gN?2yyyb70PUr&zALMM3Gm9?g>7$KKs0{b`#V)xV53@tFUz+i&-Z zb4Ixw+sD1MLVxPv`eMLRWJNaZBB5OXOh1~5?=B~$t9^fQOF%zcfG@JL-qN?W$?tp= z?sj4RdpV;+?;H${NhTX*il&D;+9U%zhV{AH(=bXIZ6SG>5RPO+W%6Nyo9?L`gBbH0 zy%&({ytb``eyy@|D|DRA6~txP(<NP4U)!msD%|60xrS`+F0<-k>)*mp+Hscq)!#lX z9Wbe`wIcjvDXvMra_)fQXUshTL~fGf+&=m5l8hW~$ta1_cW(W^>(588a~(p76Fq%Q zN}ou!mHO57&-?*ic*^!e1=>&qi-y*vg;3=vyz`1u&r*7-=iGOOvn<EC-+I>~XS}|Q zID7!kae<|rP{qd1`b5m)xM;&Sd{6#<b>39nJK%}1b^QUJw#fc%V>t9Mn!azTCoRKm zhO>K)v#iM39`UhQ9%!YTVBu$|<A=hH$F;rhKIor$Y-nVuILG;NQR(3^J+XA1pdj@A zRqns5-1;3t5r^aaV(O%BUb5=M^UN59K_N|0K!fi_O_JWKTWy76Ldv<4!fmWDrZ9Oz zm1z20X4R2Hjl6Sxq$*Nc=*^58<u?;zvf)M6y*vEoZ`RKe&vGf8#Z)p8X-p$ShT*N@ zH0^^D2G6>$=QakL(y-hJbOFr^FDq#p@6HlOGbw4}rg3@9EGYRJwbwVro0-2BKs1$t zAtqWpyV97(#Bdad5OJ9$XAd)YBsZ49`|%2Up(k@iJbK5D*A*6T=B0YeG-f5YP^|Z6 zUKGzie$40zhW?q%hKns}3}hu<=B{$s96(KIJJo;CgQF&o_8$f12MCj=$W_bKE8ggr zD_-fD$L0pU*l%TIo-*QN>5C&$j!H*mxzUJ?k$LQk{Whi!&T`5{x5&exFM?STM5l>@ zf^rh2N-rvYQ8*a+OpLdIrPy)d?~GW5<51x&*`wj?iGbq)9t|M`FPPcwIFMC|#bW~H zyFGEXBlnU=TBF58p~B${m`+t<&*w$ouO9pTxPjjXx_|E}8A!1G^Eg`2=tI7bl=zHC zZ}v$o=|{fL<iDRL@yTxVAU?}WWaRs$l7U=FUoy}sz7P5SP5<vV^G@GM20Ax_=hX!_ zK-A+};zS0z#dD<pcS0mz0#;SJDA340xHKNvR~jDm)31u71+i5HNyDuZz`bvCQMI^) zHzcUeB!suQs5)K3FD0mMB!rBQPNfItIBmtBiiFFo@$|`1oKaaLl=4_*K+O1r)Ci>_ zRv8$Rov@t4(*uN_5o2r=WRmd9V)-<A(q+#A;ozHU(ZsjC<K(H~e~3n0_%-p6$nXx& zp7wNG7VPASp%+W9n*Aczz3N(a#H~o{^ds$fx*&`vp$7{6e{Y;0EilRJf>Nbduc&aZ z6<0t7B{xYiDKhp*SL_jcutZjfd4c9tQ~@;_^x?VdjtmMykQc4zidS7UbSlx%du=F8 zUUpsZ;%kJY92ND?9=tc}9Ks6e%o2O#A%Y9Xi-x8G(~kO~-#N84_HX+Ny-!~yUblMo z|GE!ecItoG%jt5!z>c22*#EM`=`zSijVBozsYxY6Q=Kl;5exuOFf;&5I0Ek5JyLU| zHCMdijewP&rGe==#d&N+WWmbf7yIRo)2%OFPkX)L_2NoL$M5$j(MCridgPodADqRn z#H=NJJt=je)4|C8mpP})J`^!M74E66(`6BW>ITA5Z-{~DmdZeh62X3+;Ya4^M*;~= zR1j^&8?G^IOuraqi~|Oa`cUrREO89RT>mUdgiyOICW6*$?;2mHY!^83aBN)v?EOL{ zupLhoY^-=yBE&dajH3O|zdteXCkFn+z@HfS69a!@;7<(viGe>c@FxcT#K4~z_!9$v zV&G2<{E2}-G4Lk_{=~qa82JAg2IAk?eelhFQrDxS_MDg<vNP+ack3E+UYqHDcgyWX z+6(O14IiIfKmY2;gmM4;@$=WW=2ksF!#L=RC3EW)4IlmGP-W(;Lo)V9Mfg7Wt-tE~ z<bmqAzg?8*zWHJJm#>IScVf;j4m>%!+5b%VGal8a`?l7;ub&n6@-E9SwsoH@=R6t1 z)F%rgt(!i5r?~CN;HUCm`bWy8e$id>*kjI@T~c2t^IDN^|Msi<ZUJP#y|)is`MYNF zx$>kIpAVeYT$}UO@jj;}=1qET+lcW~rnf7f<`XwQ+qC2B1@GM)_Uo#J+dm9m*136_ zxkGk+^<J^}(lJl`wcv1x--Q1PO1n||ScM;N4$-i-%^!{L_ufkJy0`X6fBLL*j?1e* z2G=e)>=Wfz|IflZsu2^nB|kML^*if7&VKgmrrq`(&s-=e`s!`jZ#NGb%d=MBi2UVz z*oT&(rDrNnm8}`*Rs6=1lk?u(IOA#kk(9aqSE?i*tWCV_IeqW9{D8lH(;Cwy-x~DX zI~9L_w==yt|Jway=g8-S7L7af)3o&uUwS^_M1<|$mb(kRzn=Aa+||?H$H)H7wV~-i zwz;Ko=M!p8=#zbxB{8!<R{mwfkU1Tk`-H^>+y6`YiO1kCCQtqH#qivb%U`~HT{qt_ z%JjmDx}&d*eZQvNH{-#SRr}n2-Ze>a>xZR*0ms=(+KYQ;=4Gb!tseebf2YwQI%0N7 z|K-2~smDJq=N;U2*BWihp31%2<@?QizvrK;)fZg9@>AiSejnT4-8&&pv{QNF{$JGt z0#1GAzUX1_%<~a_<?n1?Q^9VlSCnP`aP-pB#`lBftkVv@Gvns#yZ6oA_{H=WzpcCd z%$ql4CvTnU|Ci0H4;@Z<;>Y)zz8v9ve8UHAr~i4bIce6jQ#4Vo6H7l#oBZU0zqgEi zTI~7T8_CNP%a;Uxv|`A-d!MJeT^Kdod)Ze-JHB`N`mLwpf6W-5^Br#*<G<ENGBC92 zZ<g6--~O+kH%*&Vy``bgO9?e$#Yc{f9Qw+**CIRfa~F0nA)j2_dhLbERbC~Zw#V*Q ztsFM`YStj5;pLdct?R>yNCe@X|7;$Q`D6bP4g7;&M4s*K$G2bi^21Bs|J_f$B<7J{ z@G|8+e!)w~Yu)~CFY$Bud%T>B;P3GgrXTe`@)FUQKX?hb*gKXG$@X4}Y0{~`e@k;B zKL~bjt8IDJyDdJSe`?&g^`H6XzY)=Qx5uuSwdWNd+ssdI|MCm+_~>?i{^;Fe*V(?l zx{df$G=G0(-Z%Z@U-@k5{BtxbQ6?8rem?1^)Fmks99f5LPN~bcPLE$LbNI=gJNB-& z7k{l<+U+OR?-tKF_LEN8<0s4so$Rc6$|FBv{~C1ft@_{ngx`@K^i?lE@!uAFptqj{ zpZo3)elo7;kAAZLF+W8QyLQRgcUG_2^m={M^e%h%-@_h9_ixX49#Xshfvf!{9_$(S zqx_$%ZX3eQeQG!P^u)_1zW79S(Y>W#oDN!WT_(+3dH%TlwV}RFJ@JpV2ibifzTu~D z_CHJr4=Q~3>W;oY=(e@{vH@8=>2tn!nZNeEoch9F7d<`t*3_C;Qu-v9<+5AfJ-2$0 z@9EY#6;Ex5Esq<u<kR1*AJ%;R;ng{|w3r*R8?)ZhKWqHr1^;&|>YUDs=B)PkXzlQz zk6-3fmp%Vc;lthE=yq@ywl2G|wV@$z>*bOOAN|s}y`gVYH6OG%?CP+(HOmq&yknkw zzVzOTl;meB7B&vAJw8}$KK9D2fBx#$+Bl(n#}~<&C%K%^P_MPF`vyk`_&wRRFl>sa zd-SyFOI@?;UFWZO@`SB;ZCIhP`pQ2|le7bB3pP%&y_uYU%=e1%Ob}Q3<=PQ_ZWdI{ zmwGq6<?Eb3>WRrk69<2I$Q*?iNn#S~&OSHw<hD7#E`BoiXZ_!nEzYc2>9z9s8fWHM z*?s#*h9C2j-n#SVy^lIqE@@PTk6hk^EBY&T?IUsP>owpBAHPA=KgjQi0X|-0($}qz zs{}X9Ya$Jg%!_b;(aYaE?li)W_KG_U_lLdwJ!yA67FN^k-;Uomdc~E){a-K%i&kXh z=Vi&`=1p6ar`5{gTMo@uL{qr%DVQfS;qFN@{joGX;XU7GuguKO&RQz(PDh?gojjS= zIWlhI2>Kh*{R_Xqa{<5b3qKX(pz*>lO%^w01R0-3O%$QY;-*9rb?Qt<d>SDW(;{Ia zV1^?eX3B69KXDpKn(8RC8#Z!!f`LR%G>|7@LScen2Ehz~=?5c)af9JtL@-@Qe+Q-$ z<~q#r$IO+-{NHu^zrpXhUU8qp{VB}r3BQ{ya8vW|!XA(7@q0`)5Nf<(2EveT^Hh=} zoW}XX{UUy!gHd&xQTTll=D@t)&B+V{IR^iG`0WWloNgf0^!R)K_Jq^6>G>O(Z6Ghf zJOlHl7UgxjX*dm=fUtAjZtC9#vmAz|eGb0~FuniO+T1A_1>i-GUtCy}QJ5u9%2-M# z<=BkO1-<>_mKPRgFIpy_g0$-RiE{PQrP(y$M<s?w<{;cXW&_;xFJPU4EQ6uH17QeE z9?Tf(#^3w+?J;k{y$5E0cNmRNTD)}G)Pi{li<XR*Ket#OyDWCOcBwpLMMhqJh9*B7 zV`Itkyuugd)AJT)FJ8V(p0`xq)Af4B(7ZfH8euHOrc9YPE@M$<c7FGl|2|t<wD9*c z*}dC?KtL<y9Ki%4r|h6ABJq`wXH_ENOo9@Oka-PC@K8xfxN5qy7s>Z?e#tTYLmY*W z|B@4u|Fw4z!7mAcA@VS?e&qVna3WuScWo3QRfI&-aPl;@CaPdHJH?S`m57;0N~fQ6 zo=kMAf7T@t9bx89C(<N$GLz^gdXc##<ooq*=}1qAV}ZyO-1&lyhQi-6!PXJGGpGT$ z86qXgI^yd^B=Gu>EeO*QsdyjRONg_;>?(ZmyVXI0h>BB*Aez(FmqZcW!&hcMP2|!Y zjd4g+HDo#wC7J<|9kfb1`Rg>N&@4jc_a(VxF&4K1!n=#eN+KG7>?P;cFDdqtJUj-j zQOSobvrmUea&Epr!XzIawUThjw_jzs>c|twbq%3}S1F+wEE9+#%ouRLO2C;&DhMTm zwh=GbRm2naTf_r)EurMaPSlVPGwB2S1A-Tu$bKr7Ob!t@*jC~S`v{?g)p4{RAx(s? z!ObxEV_3)UAM6ey5yG7rpbV-IsVx<3FTwT^Y=6O)3wDrThcH-RNSI)U3wD%XM+^4T zf~^wlIKiGM*pnIB3Q2-JU9e{g_FTbE7wjy-&K2x@hQePU*vkZarC_fTY@J}Q5$saI zE)yErAlR=8c7<SX6YMI%Muva*>mY9l;k67Ue|8GCS+GA4?EQj$NU*JfeMGR2Gn8&> z66|Kd{#>xn3ibuTZWZjyf_;T~fb>5Jc86f!6zsc#-6hyWOw&jN+gVHrG+wZ!g6$>P zK7#Ep*mA)R66_H1Z%7{|*x`a5CD_q|P1p;AF-q8~;6GGe@N^Joc0E>6o;?9u%DRjs z#EVUU?Zd8z?a!WoEoWUu5fa2Ezz$*8!wzFlzz%2KVMno3U`Ml?U_Z^ChOJ_gVaKuM zuqU!-U{7XWgPp{F0ed=o4)#oTJM6jaMcC==>?0WK><h4S*}uPqsfGP!4O*Xl0z|_y z_J#3;tYrVLBxDt<K^h&ag}sI?gk8#7-oj{NJJ%3mU?Y(JRkjf6D_9HCZ)0a1$7o{b zz<!IJ2mA5A5>m_lqZZ?f{k9b2iyf4Q@x{(d!1!YS5sLA}X5uR=y8!kPwg~odc3&;V z7kj4^<BJ`I^q;duNPm{yhx8ZNJFr{XQ9}s1%of4E!tR6p6MF}C2RjPv<u}<P*mv1| zu)A10Y>VY_xc<NO)Ab{%aB(IUzj>f{<|SdB%w9HHE}x3UXR)?>feIkgau+Yp&ssEi znLIb6pdfpZE5Pig>E`8U3QG}D=jSiZoHsdd(SmV{7cI+PxlA6fPMADCE&yqJXgl?| z$ykzU8S>c0%NJ#3V6BVK&&Z**o;)sYidvq%C=(IOvJ11b<Qa?P>WQ)P?83sug}>)P zQ>ElB%8^fhu>j5fDDTOW6XIw-z5gz}XCQ|64P-6M0vHX<R2UV^C>R<)48ITYdkY48 zYcY#|B4QXuEEW^4U&8EwMWwo-X~jG7T#Zhpv)WNWCQXN5kB>kD!n^^q8fG!f9GD3( zkucvj{BC}QdyMUOKaG2;+aHME{=MSd;I<>a4!=F-E4ZoY@zdWP|Ko8z{v#h72sI~; z7|1_+nV#^ky5oENJ-;+<BjTpZ84?B)*UR9=chY10;m*Q3yAoy@4Cg?8UuXP=!%+V# zFuG?Q22MNOEOyZ_yO@Z4#1g0=bMhe!4JJQ-W+0xQ8wlUaTtk>E{Pb6h-!A;#>NY-G z48*+EKy)`im!%kp7wA#Jz1u*ndkm!M0|N=UXdo+F5#DAXRev>*xdum?v+!qq?{J5` zggya7zg>ghif;Gk-sR-;{^de=*H=h?-awo$7|29YPCA?%;S>GJiOS#M-l;c`sMU^q zXnn>azyD<BJr+0XG5>%1j)o_}#Pv!)1#UIW7rz?FS(qPT+PZ(wcZbpN-liulg|p`` z@wS2VnCIZ8rpHfzd%}8td*XWh`a8fe%r=;pA2St?`Dy&CFou5<{`j~5xBjyK2W6E0 zhdkCGyytKBeFL$;?11Ta?~i8NzX>ZxSjoQ$fBf73TmN7FACyt>AM(gX`2Y4#>s(HH z%sH2G5(-n*D~$T7=}FW3mxedWem5uKrsm&;Js#KN_w_9&JtlfUIWfUcO;1=#FLzHE z{iSJo;(FJ0dM<Wo%d(d)lP_GHm5uEhT_Q8&%L+3VEnS$m49l1h`{<MCkaE&vo`U;Z zn9pF2!PLXN4^sv68q5ZmRWOTSGGS)I6l5&Q%N#AAI4(gxZt=o}8H=*y*g<MhO#Jfv z{QuM5*~dp!oO^tB_axaQhhzf*14KByh*1J85P3724X>dFOxYmOQj<smNexL%0)nF1 zwp3}wmR-<NYiru7<@WXkdudC1sjf;bDpl&G)>>|fmRqr+M5RLOn!Vp=_AD$B!M=Uk zKX$^8-!pH|%z0+coH;Xd5=$P-@ien*J8H+tJ7nJk2|DJimY3i@2@>Kf<vLnh+TNEY zenCt>qtW3E>NfkA)yhT7)0x?Wf7X>&tA5(I%bRR-ONZ6Is#WyK!@a}m(AMKs*PdVK z6f2k;H1PO8=XFRO{(R)--FFq|Jhye>qDpn(tLX=(V^7eRCG2dF^oi)JBQY*fj_d3j zmgqm5Ecp{%@baMA41}X4JY&FBK>YJ;7P&8G5+;a;A4!noVgC%4I)0t9;;wId#5-xM z7g$a8%_}+@tftzw6>?`ZFu}Vn)_r3!YSxtt7L+cWZTFgZ`ns0-SnpX`+p(;H$hA!^ zwRQ2>v2>T(x_V}atmQDjtfS-iQSiY9esG=-U8wZ(zqZT|$n&AgjN99{$gg(&cSy~K zmk!yi%HeN+FQh8r15boh6<qOHNY%jwKMtt|xN1j8HNicVj3;p2oFH>2crNoKcn|3V z@a!Lj)CM@$u{mCx+JgVcqM*7L4zG*pIkqIGXA^oP-!}A1Ipu>lE2&=x^%MPh^OzgJ zvtNwqEkN(Ame#dxjVl^Dtb%11S(CipsfAW)2e*GqyB^?HG2GR5G`2Li7qSqxY`n!w zh84@uR^MLVwz|Gfex1olw}dx<#g+5*I`QIC#spl2Un&39{#kS9%`UAh#m_EG<$^`3 z9A+R@yaU<sd@o%w|4PZ@FP}4~!au8YA@UWA7HWOv3;b%af2QKq=*r~_+VwljBhhzV z&qR+#OQO;H-p)SUTYUTrQCXddyBdVjn^v{e&)~hdewj6i-=*sHPFID5PntaAlIb&w zcn_u?^|h^5%j){J<xMR&;%9AeK}%b`UI%Qm7S%6nX{)nJ<I9AxabEJB;0wfUtAnaw zZBVTNdqDY(L1nB7s=`}?>d5Wzx)|2o6jWPp4yxURmvsi!`cKkc0rGEz?~M7oKFwVA zHoj`SBZj8W^4<LB=^u9mmG^G)KNwVX-$XAMy)CHnZ4|8!Deq@Os^&J@`gZ!oXTcpI z6@iaz45>YLhSb(Cgw*;_NEur~D*tZ6KOa)lwlKz_ciWeOs%C3Y72iW$@1?9S^BnvN z*8_X0)2>fZ57=`9V~xOe3tJY}^7uIEs#}8UX%IRKB+ZmQh^OxxF8*`ri{}-V^xi%k z50|(?8}W2;zi?uFV)#Epma@)N-kHMUb^D;WzovfY(joC;ue0ftG<|Wl^#3k#a$R%c zs~c8%Et5unM{P&FMek({vivtT%F~TUW@}Tu%#b=_n*e%Jce00{U6@_Jx^Y=OI+~m7 zm&pXHjYp%^u8WbmAd|A|WZgw{GOE-!GJsnCRqZV4)FGWJJ2o?<Mh#Y@y{9*`u8jF6 zn-qz8Ww{3ht?|lQADBf~oHY}p4v7U8BX-+5jpf?!GGstaVI98#cB&ME1vdPt#wXx3 zm1bP8!`()c4o^2$YR)j4b$F)HqS-WBHTN@a(41w+#K2J52HQL6%`s#T!Knrqt27TZ zR>LXkJVVA^Lk%|8XdYs$)jZU=Nprq&v*z=Sb#RIrZrq~TGH!*_RDmJu9BJwzBcS<W z<CCycjWs@{xzPBu=5fY)&Et*FXr5r)rrB%Uu6eS-@*??)jL*WXkhnjm+2y`Nv*F$d z8!F9xC!C_v-Jge@D%1T1?dLBuY3}EiQ8GnkxkH+>-SQN7DUW-L<{bB3I^1F2t=VOM zQFDs96*iRH{F3&koA+qWFu$xh)4W%6Kl3Y^bIh-5?r+|wd4SoaIoJG}=7HwdH4ies zq4_-Xe$9F21DXe$4{9D_epB;MbDQRT^FK77Z+=VjF!Mh(4>!N9*)qSQd4&00%_Gh4 zX&z;6*F4&MNb?2e_cf0({|h$Mh2{>;1?CTQ_(kRqHD7H0Nb^{8r{+Ra4qv1w{(zz8 z@unQsNKq5aU79DFKh-?hd|2}(<|CS?m|@LR%||s)Gau7D-F#g040E^UOU<8YE;gUg zTw?xQ^GtJ(=2_;GnrE9&X`W*~t$D7wSMxmc7n;k=XEe_@pVj<PbD!o#<}YEV@|(|T z|6=ohG+$-z*Sy61mF6n*dCgawzt((>`GV$;nZJPzRc*cqJ5`POTOIE39MJ6a{I6!0 z=XaV7&q2*8o^H))o|iPIdtTP;$o;)$XYL`*uH66AoRa$o&8fMsXim#Ltl5$Gs%B^2 zA7Qt0<-Ml;M&6NFeB||5eB=$yuE^0?e56NnO5{z=sgbudr$ye@?2a7MoF4g;=8VWY znlmHEHJgzWnzJK+)|?ahi{}23cQp@)L^S6{-qSoN(yRGA`L!kVkGzO%z@@0c5jm!n zqJ~6dn<_;Ojkq-DM_BO0-y2EMJS>u`d3YpEvlVe`9uY~`JTj7@c~m4*^Mw&pb3ufS zNc4CkS(>LrvNcbQcwj?SL~^wMqmlla7exkW_D6CxS4IYEUK|;u`KriynwLcKG*?9i zYrZ-%L~}GU6c*Rf9n^b_dv^ut7wolsEy%dSj?4(=cJS<DLF&cMP_>LeEUEM3RN17- zBtC;UgLPpSV`Y?fHQ2j!5o!?2FVZ8VAN!$M__KkBygB6VPxt`*x#S$E&ST${-(dmr z_<t~ZhLCP3dh*eJJ{U%x;goGr$_P@AMB6Adk0$H_Fa}%*3cy9+VlWmIQrb9bG@iOl zK(7~_6DemBVUww45%MKq3VNqfr)kt_I%Um3zLfZ4!Y`xte<5!#HI0J4Zli5K1&jT{ zUya#s9d;A@RUM4kuPhv-B=)8r4x*KPse%xt;K&zuvJiCIB2L0P#C+LBX|8Ru=E(ku zyg$Xx=9Dk6Dr574_wSd?^v9m@)^+u3TUcC@_tYkN#Iq2zqMmsQ0cTGkY3vlWO)~pf zi!NCYTGd`(rxrI0SqW;|>RG*F(}zf0LWEs|+GWe?TRY@%#Hl)*UW50iNT`Fo?9|w7 zX&t%@dmdx0pn7_Ru7P!k*agPtIMx~J&e}q(;L4U$$=cd#*ILV2d(&$VcE#c+mts0k z=8GHEwxw+gS+bx4OsuTaZ4tLsyt;|8@wQx8e`CBZC-=chmo>%e!Nn%TpS&b&K_iGI z-tQ`3<o$3zYmL7Ss>9FoE(7X750JR{`_=-s?JyoMw${wXj&@asolgs}RUi9BQ0=52 zKMX%$%eUAp;a4LIME+aQpS=6_{%v{V<!!a|&w>RLz)(9Zo@M}kmmT(}r-SNM@GI~` za3{DLlz;+|3lw<msi68bcoIAW?g4dR8W;rJAo3*n9>u=!%iu-u1o%FPm+dEBFamTD zzS52xjK4M^-9+TxJ-n9_z8iiBh>kGWjQlz9NidQ47T5zXhZlk(Fb4GgJeKEm_&HDp ziorM#uTPvKPXyIV;A!v;a2vQ4Tn`G*0FyAWnLu<L|5;GI0<NVVzlEO#w}bt=V|D&9 z{Gcu02u}kOzzC2I<odt!IBOC>^nC%o4Kx7<_$gs;Jr-0y0C#{Ea2;3-Bz_h=1>^$J zB{mX0CVm4%2wMT(0r6}8333~VJNtc0zXi>2Ufy!{H!hxyuLaeKw^+*nWj#T)<0yW3 z&6`2h{RZuTU(%d(o{T}R{M5nbfLKVSe_4xlndKE<`K;=>oEwXsqO4w6zoMn1QLmK| zmsl^^jc;pdI#<<n0S@IG9=KeK`adVn)x$j8D;yyu&*4{*_aZxaF8&dD-GC61p<Y8? znj0c@iaLUPj5KVXtJjgePD!7p-ay`noWoP>D6+>Ta=Pk4?$UCGdK3AOmNV5`$fglO zqp99T-jAHaGyE8GZi>iR>QBhU$TGip2f0qm9(5dfi%yfHP9X0>b})<hGx8zi6lN2D zK{iuE)GSxMi`=2*fhvN$9XW%!#e2wG(j;%5>P249+f<IsIFOGa^Cw(nvZbu_5H-u^ zt(LVs<Q!%pa%iem%fpllc`LHaN(|(%mMz|LIVXbbV3v}KT$3U4NZxjtPic7+=gc^R z(@*k_R_Vw~kyDw)WFYU*@)(tg96?TJR%0T!4iULP^+OIL8_aUDkgKvpzL>XR>Vurd ztjB|V1lc9CALN2;DQleSk6fYU@w_dg2id`_C>MF-P)YAq1CjS2JJduq2>FPXC-FAT z`5=#kPgZ%z#raa7BHpg4KjAKBQA3csv^+%(MRw;%_*Bl)asCL|!R+dM<S=puv#epr z1^p#_25;ZkMEAW*m4#e^>|ypb0=bT|3}#^?kt3$G#pP-g@_sFssL{yxYMEc;DCxCa zs>UFXMmAKZ<3i;8^Q512It!53Bb%zzbrEtwo|cV^k+&kdRA=T`<g&q1hfcE)+0r)e z>^Dxrk<(PCXFT!_>V}@23CK&&7n^hr^dfH>sO3Quk-dY&{+;JdLVkfhfSflOxhPZD zXK)enw`1~<OOV$ilV<1?<fSn=e=73!Sl;ueA@3)gdJdb89Er)pXCQk>L%4M*@-$?n zu8B&xoBD{8DZk7|Wu7Wbx7SRPlk-#wmwBqN%u{7PEAv$0bel75&a~OIxu4BhHfP)H zu{p=){x-`nD0*{YnJ>%iTKGJh^K2drOFHgOU9WsQ{Ct~-*(~n}GQYNL9%1uHn@7Q- zf3(f=?jYAG?+?QA4k6{pdxWsOOGvo9PYBC9h0NOvZ5{`UzVS9sfF+*{=)x0io@Dc6 zn~Px4cZtnYY@TZKG+5pXrrSIN7JZl6Tx|1YHp>t%@g+9<Y%aCAlZKc0P8wb_4X>Go z*Gxmob#&5@a($gNq-GjU$J20{X&{|$kj*rd4yU0s(@>g+*i3_IKMkgt2GdM~X{N!X zy*jNJUlZm2;+g%ox&uWawc}Fu&%7bEab`%Zna@7O!WjNmooC9oh|Q{w4qk)K6xk<i zezVoIvbud)OZBpb71gcRcXV*ht-7s#WlKkW^^LWS9o2e0w~(z<R*+|1SzSJV;T6@h z%1Rf`n-h~ON*9$b@K^iiRO%h|x|U{{?k{e>zPaVbW~+QbMeGQkeSWS_@iA|IPnMbC zPBrAlKa)FG{52JGizgKf%at4dAL!D8)wc!j<Xr2=-+caAGk5HD*FJaj*aLa=f|wg< zwWe>KJ>tQtmir#>y>-?_&jr5vK+XjxWT{BIdPcM~hr@8BFto>i;*oG8@hJ|c+mYeO zbefJVy(p0o&&hA%9cd1?GlM}c_LC5QC-F|F=y$nYrpx2X)kD?E6rDQWWw_i<)0yS) zIQugxNpN@Qc&Fh^bxH}bpM?0k65`KP{t7!jE!C0kID_qFJ}32ey3;b8S?{m^NITx) zPD|I<FISl0CiICnJ*UTyx68NdpO7;qvYw57QVg;0`yvT2XcVbC4~|nm2?_Ivcc(fA zv7~eACm}vp>vv$kgun!`pB<mjKJutJ=?W9-#~+l)a(Yg0pZ>&){Z3}+BfQEYpTm*< zL3a)FOlHpV@caN5IY)dEi-!(}!^s2r)K7x0OU@Dh9`)m7tL|SXH6-NslAnXG1`ToQ zCn0{~IpU+Xen+a@Bhm*QnGVy@&ynTGc8H~QerCp}YJZxoxN~&<PS1a;0!|(8Fj7)d zGEeF_6^(5rzubSPYmgd9vW$5733(F5%yYzNF(CG_{%Q5kK1aL<{nCC8mmY&+R!FEo zj*d?V=pzn1NBm&QPdTkdrz;q8j{HN<5kE}F$LsH4lsf(XAHhe64kJUBcu)N#RN*3> zKl1}?c=0*n3;UEWBbhTpdRVNGaXLPsz&_%5;-&xh2|nq4aBE1t^~I3-GJGcxf2Yki zz}JCFFc(Y%QPL*5nlFXa^Mo&k=K=9gw|Oj_53+y)Z;|fzAkq1{LTVuB0ITkcVJ&<% zB<9y=Q&{+2RJ&3KFKm$$SRC@R6IF6XNStt7wxcvPxMK0^;c`%svRNG>?JhWuE<256 zDErzbov-M@7<Uhi<=^&D?7H@SEynTiIO|OIe@r#Gm1FN3(lCjBdH5vy`^L+Vk{JKj z`YrPRE8#Ztyk6>1(99AtFAt@xcDJ`%a&*aSEop1)VB&UK*dkWcNk2<3UCUR3ZPvK> z#~N~8pshW2K1oNltG9j{;(LUUx)RI+mw~BZA{YxU046ZN6Qu10iSCL=Lh9?_Ua$## z4%`e{KqFWNJ`R?E8Q@7UHXKr8zzC2J1_2Mq00!tKP25%D|2Vke(U7_ZR0{CVh3|x? zz+=E5kP6-*><{4A;OAf`*al8H$6m(^99ipOF`&S4$kC;m)EdVQ7O?JhY*4dLrTdVQ zJHx!-f=QPa&zU>#nvXTNu33BcJ>UA)sl2{fO8MrT@`ny{Ew6^uweWJAuZHt&`CY;v z2XR-%_n=n09Ku~?&(38>CpJB|WZrL$7%_sxC(mm1Jy7}nla%d^H`QCq+gesy4Jc2{ zD?1JGgP0#8pROa{!SET3%dj2K-P2s^Ib7P`>D>Qnm&PA<#>KB+Y?0`AmYmh`tpB@Q zyr2B5*yFBJ;mK9Xn(9|xU|r%@K3EmS*f)*yq|-TL3TnVUfC~iRF5sB~(9t9ErRZ=~ zagv~l#kDGxOE|~IUHjpsKxOb02ym9XGs@;Eqj~`DM4SFD)TbQjq#>uLUzPHrYZ}nA z4<Dg2@LSxAgjry$#7#yQ@a!cH#ZJ~FD6iKTshQ6%JLSERf|T<TbW<8-<J&&WAYGb4 z9r@lf?FDol0((-cSjVhV*HYK6V!zr7^1*0O2#P>4m<`H7C8z>RK@F$_4WJ2hfHhzp z2!Qoq1K0>Q0pHAQWnJc1<|yiQoZ4{Mz@Uz)ARQEd8sd+`@}=^6Y!Z%^P<iExT~ZB~ zEUR_h!}-tJNIY5|L9W3B(RGH{GR$HiE7|e@3P*<*(Kae9dBm8k#AP{ESgYhiX^b0S zttZM^*DlJ9K7zJ`;3$Z`7tN<0J%p==#hh;SHvSTDSw@wb4;R3DVBh6_^#)u9SAho5 z0YrH;S`wxLCDD>d^ayEQ2a9nhGO5?)qE|_~94w&!RDo!k#Ze3@9N-eyNZlOja<1W* zzf|hE=ahQsC8b_|8P(|Tk}{N&GzU>}JQ~)11OK~GbqiNaKjAbjepi&`V&z4DZ&dO6 zudHrKTSA)<=7nW{#LMr7K#^oB{{!K;?X7d93~CV&uj-=fgw-%9GcEe=E79mHAPUMz zzg4GIC!(Db0)%oMYQ!!Hi)M3XpeOnQGFM#lshm*(<rzf<UFw!M4s??CK8|#$Tbc_2 z4WgVD&1Jzg5%T2Er5^&mI3;Pesz83|q1bn(RoJF<5#KD*r>fC@ZHYtGa6JvW{<7fa zpc&{_q${R>G{M_MSE^G5;0<8A_~|v9U=P>|y1+iLfxMo1e!V7G0gv`nsZE5@u1e9C zv(Z@&7J!A|Do_QMf;!LyR)S`*8ms{g)S(f)3|_iveaX$a-<=m&KfW}u{_@g&==P01 z<WtJ>b<&aLdqVlTaDyy5(_$DPOzUUTdAs>XC44^q`3NuBPF~uZG$lO<*=+A9uO+RQ z=3~y;_3S}XMWfZ+deyL`$tdYY;cz6{K1}{iTqC<is$1)#(}(fWQYw<B4p8VDKylyw zo_IHmi+W|8i}f2<BVnHa`_Si`Prm|T!AH=`A)fFRoYAM&d~PjyhrJi&DhRX6{W31O zYT*0HzXfbZ?hHqx@||Tj@`JqX-pf7rAW*$a)CO#so5ncK8TQ$r4y*?|BroU@#-Ey1 zrMgMGA-hVAr+ZxkQWy?S5YL^W!lXF?qR;XNxrmzpT)+V)gO7oUU<p0C(n}k|htL<L zJ}Kz-F7&H3*o1eY(+6Aldq`6=6q^E13Uww-T}fTxD&PYZ)ae)G*$tind%!NMN-Zkm zRR83WIXPe?m<+<?-3R3GzYlbP9pEsiK~ESwNB(uR$Nkuan@;V;znt)jd7ObS3##qF zoX>tNcmdh_QTjSCc?R6icMSQkTQG|LN?)IyQ>FG3egGT>M?l_QUiEP62J_6|8rQ>N zxPbJP^wYcGLQv7aO3eigpp`Va=-NmALbz+VQlHP$_ffQr@=g$*eIcW)pL>`3jsY)| z?q%}6eII{cXq8gq@fQKcy5oo8O_ZVN#okIkf6WVf!mq*ISJ9TrpnJL91(J@ke-G&M z@17toLf8>N7vMJ_M55LbY|1YR*@nJ@*vv~Eo~3RT<na-=iL_1dHqe9IDQT){i;w%2 z!Hwks)$|V+oC(r^1NaH+CQtXZJUdC>MfwhSH~I^ZrH!^zMi}e^0Z9XXi98AHM(zfx zhH(d!fjSTX_kvwuKR5!+T6BV9PyxEhAEK_?kaq$$=v4G=Ou~GDtyq;hneY7x-yr3J z0_?n<Ht3+APQwlXX+O|P8iRhi4P8B?J1l9zI_b|ts?@852S~HMj(c3f=<C~H?ArSV zaaKKJD)2GS_z7k75>|-6oAhPywq{1KChAQ5HJ}Fh3DOjk_W41yT_g4<-6G;-uSoVw zWbZ}xOJr|G_J<ZPW{;5g0O`0hOIY#tb#k4@(6b4B7InHG-U)UCrVP<mc<(^!$pOje zQT8P9k0G7>;pRigCxCCbOeNT}9>7C-rC;4jeeR)OEJc>T&$g2XX#lwkgh4kr2DYQ; zTi8hL`JQUPhE}s*l`!`S?{_F4@*#<1;V{raK2Sqg1^0j#YzM7ei_A~yC~6yiwF;e# z2|ic_s?_7e9ewyazuQ5&p4Ego$;-2($Q#SIleEfFrTWPTA1Sx?K9h6s^@oPMblu~W z1th%_Fx;xZ<^#E^n`Z!dPM|9<OrBeLO5eygQozJ-NLcrHWsTq-M(!j%#}rfm?4%BE z!aS1ZVUmqx{36Ut+5+;=7Cnq5hY2s>o~WRX3xOg`cS@C~4CF9*Jg|o`fP;prf_!gb z|4nch^ngbnrL7oyQo&Le*8uaYVN^PBgESxqi6486`w%vO1Gs>E&*@vk^@D+w(*&;r zAEO-~kg<~ZJi?h?a1lIn<(bya+@7!sVBoKTYfS1yzMWh{<s{PZhfR0O_4GQG58gkK zb^?o#%fP|MvDIjFW}!0!@T6rz9jN&u-GltBoT@#fKfpL$1(%a&HN2X*a<~{i3W|{x zY=Jj~@oyz<AbR{hpK@E%2-|`mw|)z8dqEjEOxRxHT0xk48?^gw!t?R_!2QVg!@q;~ z5y!|zzj!wKJHiIxf0}U0h(3vbeoQ~DpGLY9!=txzZIpeOG+p$`L$D{)rJjOc;Ch}S z&nDy^%B@`MS2M}~5s-&G7v4&|B|3XKqYBr78nA1~X4OOgJqCV*Jd*v99=Qj=I%Bh3 zW7rp9h6HxGHgn3vP~lF>x|z8f@D1Lq0;KH*-qg*i2HtcN{a*ES%7?w1C_mcU+Z({q zGYC(*qy>@|NLnCifuseJ7D!qkX@R5#k`_o>AZdZ51(Fs>S|DkGqy>@|NLnCifuseJ z7D!qkX@R5#k`_o>AZdZ51(Fs>S|DkGqy>@|NLnCifuseJ7D!qkX@R5#k`_o>AZdZ5 z1(Fs>S|DkGqy>@|NLnCifuseJ7D!qkX@P&F1s=PMUtXf!+osc-@ASwYlleDt{|~7O B&Pf0O literal 0 HcmV?d00001 -- 1.6.3.3 ^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH] patch to request new firmware for AR3011 Chip 2010-04-27 6:19 ` [PATCH] New Firmware for Atheros bluetooth chipset AR3011 suraj @ 2010-04-27 8:28 ` suraj 2010-04-27 15:55 ` Luis R. Rodriguez 2010-05-11 9:04 ` [PATCH v2] ath3k: add support for new firmware suraj 0 siblings, 2 replies; 36+ messages in thread From: suraj @ 2010-04-27 8:28 UTC (permalink / raw) To: linux-bluetooth Cc: dwmw2, Luis.Rodriguez, Jothikumar.Mothilal, marcel, vkandukuri Signed-off-by: Vikram Kandukuri <vkandukuri@atheros.com> --- drivers/bluetooth/ath3k.c | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 128cae4..7bda549 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -122,9 +122,12 @@ static int ath3k_probe(struct usb_interface *intf, data->udev = udev; - if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) { - kfree(data); - return -EIO; + if (request_firmware(&firmware, "ath3k-2.fw", &udev->dev) < 0) { + BT_DBG("requesting old firmware"); + if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) { + kfree(data); + return -EIO; + } } size = max_t(uint, firmware->size, 4096); -- 1.7.0 ^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [PATCH] patch to request new firmware for AR3011 Chip 2010-04-27 8:28 ` [PATCH] patch to request new firmware for AR3011 Chip suraj @ 2010-04-27 15:55 ` Luis R. Rodriguez 2010-05-11 9:04 ` [PATCH v2] ath3k: add support for new firmware suraj 1 sibling, 0 replies; 36+ messages in thread From: Luis R. Rodriguez @ 2010-04-27 15:55 UTC (permalink / raw) To: Suraj Sumangala Cc: linux-bluetooth@vger.kernel.org, dwmw2@infradead.org, Luis Rodriguez, Jothikumar Mothilal, marcel@holtmann.org, Vikram Kandukuri On Tue, Apr 27, 2010 at 01:28:42AM -0700, Suraj Sumangala wrote: > > Signed-off-by: Vikram Kandukuri <vkandukuri@atheros.com> > --- > drivers/bluetooth/ath3k.c | 9 ++++++--- > 1 files changed, 6 insertions(+), 3 deletions(-) > > diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c > index 128cae4..7bda549 100644 > --- a/drivers/bluetooth/ath3k.c > +++ b/drivers/bluetooth/ath3k.c > @@ -122,9 +122,12 @@ static int ath3k_probe(struct usb_interface *intf, > > data->udev = udev; > > - if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) { > - kfree(data); > - return -EIO; > + if (request_firmware(&firmware, "ath3k-2.fw", &udev->dev) < 0) { > + BT_DBG("requesting old firmware"); > + if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) { > + kfree(data); > + return -EIO; > + } > } > > size = max_t(uint, firmware->size, 4096); > Looks OK but your subject should be something like: [PATCH] ath3k: add support for new firmware And your commit log is empty, other than your subject, please be a little more descriptive. Describe what are the shiny new bells and whistles added onto the new firmware. The more description you can provide, the better. Luis ^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2] ath3k: add support for new firmware 2010-04-27 8:28 ` [PATCH] patch to request new firmware for AR3011 Chip suraj 2010-04-27 15:55 ` Luis R. Rodriguez @ 2010-05-11 9:04 ` suraj 1 sibling, 0 replies; 36+ messages in thread From: suraj @ 2010-05-11 9:04 UTC (permalink / raw) To: linux-bluetooth Cc: dwmw2, Luis.Rodriguez, Jothikumar.Mothilal, marcel, vkandukuri New firmware has few critical bug fixes and shared antenna support. Signed-off-by: Vikram Kandukuri <vkandukuri@atheros.com> --- drivers/bluetooth/ath3k.c | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 128cae4..7bda549 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -122,9 +122,12 @@ static int ath3k_probe(struct usb_interface *intf, data->udev = udev; - if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) { - kfree(data); - return -EIO; + if (request_firmware(&firmware, "ath3k-2.fw", &udev->dev) < 0) { + BT_DBG("requesting old firmware"); + if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) { + kfree(data); + return -EIO; + } } size = max_t(uint, firmware->size, 4096); -- 1.7.0 ^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip 2010-04-26 11:00 ` suraj 2010-04-27 6:19 ` [PATCH] New Firmware for Atheros bluetooth chipset AR3011 suraj @ 2010-05-05 12:33 ` suraj 2010-05-06 7:45 ` buffer starvation with multiple ACL link suraj ` (2 more replies) 1 sibling, 3 replies; 36+ messages in thread From: suraj @ 2010-05-05 12:33 UTC (permalink / raw) To: linux-bluetooth; +Cc: marcel, Luis.Rodriguez, Jothikumar.Mothilal, gfpadovan Hi Marcel, Can you take a look at the patch? Your review has been pending for sometime. On Mon, 2010-04-26 at 16:30 +0530, suraj wrote: > Hi marcel, > > On Thu, 2010-04-22 at 14:40 +0530, suraj wrote: > > This implements the Atheros Bluetooth serial protocol to > > support the AR300x Bluetooth chipsets. > > The serial protocol implements enhanced power management > > features for the AR300x chipsets. > > > > Reviewed-by: Luis R. Rodriguez <lrodriguez@atheros.com> > > Reviewed-by: Gustavo F. Padovan <gustavo@padovan.org> > > Signed-off-by: Suraj <suraj@atheros.com> > > > > --- > > drivers/bluetooth/Kconfig | 14 ++ > > drivers/bluetooth/Makefile | 1 + > > drivers/bluetooth/hci_ath.c | 378 +++++++++++++++++++++++++++++++++++++++++ > > drivers/bluetooth/hci_ldisc.c | 6 + > > drivers/bluetooth/hci_uart.h | 8 +- > > 5 files changed, 406 insertions(+), 1 deletions(-) > > create mode 100755 drivers/bluetooth/hci_ath.c > > > Can you verify the patch let me know your comments? > > Regards > Suraj > ^ permalink raw reply [flat|nested] 36+ messages in thread
* buffer starvation with multiple ACL link 2010-05-05 12:33 ` [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip suraj @ 2010-05-06 7:45 ` suraj 2010-05-20 16:02 ` Marcel Holtmann 2010-05-10 20:12 ` [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip Luis R. Rodriguez 2010-05-11 8:29 ` suraj 2 siblings, 1 reply; 36+ messages in thread From: suraj @ 2010-05-06 7:45 UTC (permalink / raw) To: linux-bluetooth; +Cc: Luis.Rodriguez, Jothikumar.Mothilal Hi, I am seeing a strange issue with multiple ACL connection with CSR chip. I am having 2 ACL link, one with FTP going on and another streaming A2DP. The moment the A2DP link goes out of range ,I see that the FTP also stops. It resumes as soon as A2DP comes back in range. On further analysis I could verify that since the A2DP link is blocked( due to remote being OoR) the controller's ACL buffers with A2DP data are stuck. But at the host level this is not considered and Bluez keeps sending A2DP data until it blocks all the ACL buffers which in turns blocks FTP also. I was expecting controller to flush these packets sometime and free the A2DP buffers. Not sure, what is the default Flush timeout value. Do we have any patch for this issue? Regards Suraj ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: buffer starvation with multiple ACL link 2010-05-06 7:45 ` buffer starvation with multiple ACL link suraj @ 2010-05-20 16:02 ` Marcel Holtmann 0 siblings, 0 replies; 36+ messages in thread From: Marcel Holtmann @ 2010-05-20 16:02 UTC (permalink / raw) To: suraj; +Cc: linux-bluetooth, Luis.Rodriguez, Jothikumar.Mothilal Hi Suraj, please never start a new thread that hangs of an existing thread. I do will miss these and ignore. You need to fix your mailer setup. > I am seeing a strange issue with multiple ACL connection with CSR chip. > > I am having 2 ACL link, one with FTP going on and another streaming > A2DP. > The moment the A2DP link goes out of range ,I see that the FTP also > stops. It resumes as soon as A2DP comes back in range. > > On further analysis I could verify that since the A2DP link is > blocked( due to remote being OoR) the controller's ACL buffers with A2DP > data are stuck. > But at the host level this is not considered and Bluez keeps sending > A2DP data until it blocks all the ACL buffers which in turns blocks FTP > also. > > I was expecting controller to flush these packets sometime and free the > A2DP buffers. Not sure, what is the default Flush timeout value. We don't have anything as of now that could fix this. I would need more details on this since it might be some outdated firmware or known bug in the controller. Please start a proper new thread with details about the hardware you are using and which software versions. Regards Marcel ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip 2010-05-05 12:33 ` [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip suraj 2010-05-06 7:45 ` buffer starvation with multiple ACL link suraj @ 2010-05-10 20:12 ` Luis R. Rodriguez 2010-05-11 8:29 ` suraj 2 siblings, 0 replies; 36+ messages in thread From: Luis R. Rodriguez @ 2010-05-10 20:12 UTC (permalink / raw) To: Suraj Sumangala, David Miller, marcel@holtmann.org, Jothikumar Mothilal Cc: linux-bluetooth@vger.kernel.org, Luis Rodriguez, gfpadovan@gmail.com, linux-kernel On Wed, May 05, 2010 at 05:33:18AM -0700, Suraj Sumangala wrote: > Hi Marcel, > > Can you take a look at the patch? Your review has been pending for > sometime. Marcel, new timeline: ---------- 02-22-2010 - Marcel responded to your first patch version for UART bluetooth 02-22-2010 - Suarj posted resplies to Marcel's questions 03-01-2010 - Suraj poked Marcel for feedback based on Suraj's comments ---------- 03-11-2010 - Suraj posts new V2 patch but its sent busted with tabs/space mixup 03-11-2010 - Marcel asks for a resend ---------- 03-14-2010 - Suaraj posts V3 patch with tabs/spaces fixed 03-23-2010 - First poke to Marcel and list 03-29-2010 - Second poke to Marcel and list 03-31-2010 - Patch for userspace hciattach.c changes posted ---------- 04-20-2010 - v3 patch posted 04-21-2010 - v4 patch posted 04-22-2010 - v5 patch posted 04-26-2010 - v5 first poke 05-05-2010 - v5 second poke ---------- What else can our team do? :( Luis ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip 2010-05-05 12:33 ` [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip suraj 2010-05-06 7:45 ` buffer starvation with multiple ACL link suraj 2010-05-10 20:12 ` [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip Luis R. Rodriguez @ 2010-05-11 8:29 ` suraj 2010-05-18 11:39 ` suraj 2 siblings, 1 reply; 36+ messages in thread From: suraj @ 2010-05-11 8:29 UTC (permalink / raw) To: linux-bluetooth; +Cc: marcel, Luis.Rodriguez, Jothikumar.Mothilal, gfpadovan Hi marcel, On Wed, 2010-05-05 at 18:03 +0530, suraj wrote: > Hi Marcel, > > Can you take a look at the patch? Your review has been pending for > sometime. > > On Mon, 2010-04-26 at 16:30 +0530, suraj wrote: > > Hi marcel, > > > > On Thu, 2010-04-22 at 14:40 +0530, suraj wrote: > > > This implements the Atheros Bluetooth serial protocol to > > > support the AR300x Bluetooth chipsets. > > > The serial protocol implements enhanced power management > > > features for the AR300x chipsets. > > > > > > Reviewed-by: Luis R. Rodriguez <lrodriguez@atheros.com> > > > Reviewed-by: Gustavo F. Padovan <gustavo@padovan.org> > > > Signed-off-by: Suraj <suraj@atheros.com> > > > > > > --- > > > drivers/bluetooth/Kconfig | 14 ++ > > > drivers/bluetooth/Makefile | 1 + > > > drivers/bluetooth/hci_ath.c | 378 +++++++++++++++++++++++++++++++++++++++++ > > > drivers/bluetooth/hci_ldisc.c | 6 + > > > drivers/bluetooth/hci_uart.h | 8 +- > > > 5 files changed, 406 insertions(+), 1 deletions(-) > > > create mode 100755 drivers/bluetooth/hci_ath.c > > > > > Can you verify the patch let me know your comments? > > > > Regards > > Suraj > > > > your review has been pending for a long time. Please let me know what should be done regarding this. Regards Suraj ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip 2010-05-11 8:29 ` suraj @ 2010-05-18 11:39 ` suraj 0 siblings, 0 replies; 36+ messages in thread From: suraj @ 2010-05-18 11:39 UTC (permalink / raw) To: linux-bluetooth; +Cc: marcel, Luis.Rodriguez, Jothikumar.Mothilal, gfpadovan Hi Marcel, On Tue, 2010-05-11 at 13:59 +0530, suraj wrote: > Hi marcel, > > On Wed, 2010-05-05 at 18:03 +0530, suraj wrote: > > Hi Marcel, > > > > Can you take a look at the patch? Your review has been pending for > > sometime. > > > > On Mon, 2010-04-26 at 16:30 +0530, suraj wrote: > > > Hi marcel, > > > > > > On Thu, 2010-04-22 at 14:40 +0530, suraj wrote: > > > > This implements the Atheros Bluetooth serial protocol to > > > > support the AR300x Bluetooth chipsets. > > > > The serial protocol implements enhanced power management > > > > features for the AR300x chipsets. > > > > > > > > Reviewed-by: Luis R. Rodriguez <lrodriguez@atheros.com> > > > > Reviewed-by: Gustavo F. Padovan <gustavo@padovan.org> > > > > Signed-off-by: Suraj <suraj@atheros.com> > > > > > > > > --- > > > > drivers/bluetooth/Kconfig | 14 ++ > > > > drivers/bluetooth/Makefile | 1 + > > > > drivers/bluetooth/hci_ath.c | 378 +++++++++++++++++++++++++++++++++++++++++ > > > > drivers/bluetooth/hci_ldisc.c | 6 + > > > > drivers/bluetooth/hci_uart.h | 8 +- > > > > 5 files changed, 406 insertions(+), 1 deletions(-) > > > > create mode 100755 drivers/bluetooth/hci_ath.c > > > > > > > Can you verify the patch let me know your comments? > > > > > > Regards > > > Suraj > > > > > > > > > your review has been pending for a long time. Please let me know what > should be done regarding this. > > Regards > Suraj > > This patch has been pending your approval/disapproval for so much time that it runs the risk of being obsolete. Please do let me know if you have any plans of doing something about it. Regards Suraj ^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v3] hciattach application support for Atheros AR300x Bluetooth Chip 2010-04-22 9:10 ` [PATCH v5] " suraj 2010-04-26 11:00 ` suraj @ 2010-05-12 13:47 ` suraj 2010-05-20 13:37 ` suraj 2010-05-20 16:00 ` Marcel Holtmann 2010-05-20 16:09 ` [PATCH v5] Add support for the " Marcel Holtmann 2 siblings, 2 replies; 36+ messages in thread From: suraj @ 2010-05-12 13:47 UTC (permalink / raw) To: linux-bluetooth; +Cc: marcel, Luis.Rodriguez, Jothikumar.Mothilal Implements support for Atheros AR300x Bluetooth chip in hciattach. Adds feature to bring up AR300x Bluetooth chip with and without enhanced power management enabled. Signed-off-by: Suraj <suraj@Atheros.com> --- Makefile.tools | 1 + tools/hciattach.8 | 6 + tools/hciattach.c | 111 +++++ tools/hciattach.h | 3 + tools/hciattach_ar3k.c | 1223 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1344 insertions(+), 0 deletions(-) create mode 100755 tools/hciattach_ar3k.c diff --git a/Makefile.tools b/Makefile.tools index 2735d68..48cf097 100644 --- a/Makefile.tools +++ b/Makefile.tools @@ -23,6 +23,7 @@ tools_l2ping_LDADD = lib/libbluetooth.la tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \ tools/hciattach_st.c \ tools/hciattach_ti.c \ + tools/hciattach_ar3k.c \ tools/hciattach_tialt.c tools_hciattach_LDADD = lib/libbluetooth.la diff --git a/tools/hciattach.8 b/tools/hciattach.8 index f750222..ef943ea 100644 --- a/tools/hciattach.8 +++ b/tools/hciattach.8 @@ -49,6 +49,12 @@ specific identifier. Currently supported types are .B any Unspecified HCI_UART interface, no vendor specific options .TP +.B ar3kalt +Atheros AR300x based serial bluetooth device with power management disabled +.TP +.B ar3k +Atheros AR300x based serial bluetooth device +.TP .B ericsson Ericsson based modules .TP diff --git a/tools/hciattach.c b/tools/hciattach.c index b13db1b..768a3f1 100644 --- a/tools/hciattach.c +++ b/tools/hciattach.c @@ -653,6 +653,110 @@ static int csr(int fd, struct uart_t *u, struct termios *ti) } /* + * Atheros AR300x specific initialization post callback + * with power management dsiabled + * Suraj Sumangala <Suraj@Atheros.com> + */ +static int ar3kpost(int fd, struct uart_t *u, struct termios *ti) +{ + return ath_configure_sleep(fd, 0); +} + +/* + * Atheros AR300x specific initialization post callback + * with power management enabled + * Suraj Sumangala <Suraj@Atheros.com> + */ +static int ar3kpmpost(int fd, struct uart_t *u, struct termios *ti) +{ + return ath_configure_sleep(fd, 1); +} +/* + * Atheros AR300x specific initialization + * Suraj Sumangala <Suraj@Atheros.com> + */ +static int ar3kinit(int fd, struct uart_t *u, struct termios *ti) +{ + struct timespec tm = { 0, 500000 }; + unsigned char cmd[14], rsp[100]; + int r; + int baud; + + /* Download PS and patch */ + r = ath_ps_download(fd); + + if (r < 0) { + perror("Failed to Download configuration"); + return -1; + } + + /* Write BDADDR if user has provided any */ + if (u->bdaddr != NULL) { + /* Set BD_ADDR */ + memset(cmd, 0, sizeof(cmd)); + memset(rsp, 0, sizeof(rsp)); + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x0B; + cmd[2] = 0xfc; + cmd[3] = 0x0A; + cmd[4] = 0x01; + cmd[5] = 0x01; + cmd[6] = 0x00; + cmd[7] = 0x06; + str2ba(u->bdaddr, (bdaddr_t *) (cmd + 8)); + + /* Send command */ + if (write(fd, cmd, 14) != 14) { + fprintf(stderr, "Failed to write BD_ADDR command\n"); + return -1; + } + + /* Read reply */ + if (read_hci_event(fd, rsp, 10) < 0) { + fprintf(stderr, "Failed to set BD_ADDR\n"); + return -1; + } + } + + /* Send HCI Reset to write the configuration */ + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x03; + cmd[2] = 0x0C; + cmd[3] = 0x00; + + r = write(fd, cmd, 4); + + if (r != 4) + return -1; + + nanosleep(&tm, NULL); + if (read_hci_event(fd, rsp, sizeof(rsp)) < 0) + return -1; + + /* Set baud rate command, + * set controller baud rate to user specified value */ + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x0C; + cmd[2] = 0xfc; + cmd[3] = 0x02; + baud = u->speed/100; + cmd[4] = (char)baud; + cmd[5] = (char)(baud >> 8); + + if (write(fd, cmd, 6) != 6) { + perror("Failed to write init command"); + return -1; + } + + /* Wait for the command complete event for Baud rate change Command */ + nanosleep(&tm, NULL); + + if (read_hci_event(fd, rsp, sizeof(rsp)) < 0) + return -1; + + return 0; +} +/* * Silicon Wave specific initialization * Thomas Moser <thomas.moser@tmoser.ch> */ @@ -1071,6 +1175,13 @@ struct uart_t uart[] = { /* Broadcom BCM2035 */ { "bcm2035", 0x0A5C, 0x2035, HCI_UART_H4, 115200, 460800, FLOW_CTL, NULL, bcm2035 }, + /* ATHEROS AR300x */ + { "ar3kalt", 0x0000, 0x0000, HCI_UART_ATH, 115200, 115200, + FLOW_CTL, NULL, ar3kinit, ar3kpost }, + + { "ar3k", 0x0000, 0x0000, HCI_UART_ATH, 115200, 115200, + FLOW_CTL, NULL, ar3kinit, ar3kpmpost }, + { NULL, 0 } }; diff --git a/tools/hciattach.h b/tools/hciattach.h index 867563b..7719e04 100644 --- a/tools/hciattach.h +++ b/tools/hciattach.h @@ -36,6 +36,7 @@ #define HCI_UART_3WIRE 2 #define HCI_UART_H4DS 3 #define HCI_UART_LL 4 +#define HCI_UART_ATH 5 int read_hci_event(int fd, unsigned char* buf, int size); int set_speed(int fd, struct termios *ti, int speed); @@ -45,3 +46,5 @@ int texas_post(int fd, struct termios *ti); int texasalt_init(int fd, int speed, struct termios *ti); int stlc2500_init(int fd, bdaddr_t *bdaddr); int bgb2xx_init(int dd, bdaddr_t *bdaddr); +int ath_configure_sleep(int fd, int sleep_stat); +int ath_ps_download(int fd); diff --git a/tools/hciattach_ar3k.c b/tools/hciattach_ar3k.c new file mode 100755 index 0000000..2542161 --- /dev/null +++ b/tools/hciattach_ar3k.c @@ -0,0 +1,1223 @@ +/* + * Copyright (c) 2009-2010 Atheros Communications Inc. + * + * 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <ctype.h> +#include <time.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/ioctl.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> + +#include "hciattach.h" + +/* The maximum number of bytes possible in a patch entry */ +#define MAX_PATCH_SIZE 20000 + +#define MAX_BYTE_LENGTH 244 + +/* Maximum HCI packets that will be formed from the Patch file */ +#define MAX_NUM_PATCH_ENTRY ((MAX_PATCH_SIZE/MAX_BYTE_LENGTH) + 1) + +#define DEV_REGISTER 0x4FFC + +#define FW_PATH "/lib/firmware/ar3k/" + +#define PS_ASIC_FILE "PS_ASIC.pst" +#define PS_FPGA_FILE "PS_FPGA.pst" +#define PATCH_FILE "RamPatch.txt" +#define BDADDR_FILE "ar3kbdaddr.pst" + +#define HCI_CMD_HEADER_LEN 7 + +/* PS command types */ +#define PS_RESET 2 +#define PS_WRITE 1 +#define WRITE_PATCH 8 +#define PS_VERIFY_CRC 9 +#define ENABLE_PATCH 11 + +#define EXTRA_PATCH_SIZE 500 + +/* PS configuration entry time */ +#define PS_TYPE_HEX 0 +#define PS_TYPE_DEC 1 + +#define PS_RESET_PARAM_LEN 6 +#define PS_RESET_CMD_LEN (PS_RESET_PARAM_LEN +\ + HCI_CMD_HEADER_LEN) + +#define RAM_PS_REGION (1<<0) +#define RAM_PATCH_REGION (1<<1) + +#define RAMPS_MAX_PS_TAGS_PER_FILE 50 +#define PS_MAX_LEN 500 +#define LINE_SIZE_MAX (PS_MAX_LEN * 2) + +#define BYTES_OF_PS_DATA_PER_LINE 16 + +#define skip_space(str) while (*(str) == (' ')) ((str)++) + +#define is_between(x, lower, upper) (((lower) <= (x)) && ((x) <= (upper))) + +#define tohexval(c) (isdigit((c)) ? ((c) - '0') : \ + (is_between((c), 'A', 'Z') ? \ + ((c) - 'A' + 10) : ((c) - 'a' + 10))) + +#define stringtohex(str) (((uint8_t)(tohexval((str)[0]) << 4)) |\ + ((uint8_t)tohexval((str)[1]))) + +#define set_pst_format(pst, type, array_val) ((pst)->data_type = (type),\ + (pst)->is_array = (array_val)) + +struct ps_tag_entry { + uint32_t tag_id; + uint32_t tag_len; + uint8_t *tag_data; +}; + +struct ps_ram_patch { + int16_t Len; + uint8_t *Data; +}; + +struct ps_data_format { + unsigned char data_type; + unsigned char is_array; +}; + +struct ps_cmd_packet { + uint8_t *Hcipacket; + int packetLen; +}; + +struct st_read_status { + unsigned section; + unsigned line_count; + unsigned char_cnt; + unsigned byte_count; +}; + +struct ps_tag_entry ps_tag_entry[RAMPS_MAX_PS_TAGS_PER_FILE]; +struct ps_ram_patch ram_patch[MAX_NUM_PATCH_ENTRY]; + +static void load_hci_header(uint8_t *hci_ps_cmd, + uint8_t opcode, + int length, + int index) +{ + hci_ps_cmd[0] = 0x0B; + hci_ps_cmd[1] = 0xFC; + hci_ps_cmd[2] = length + 4; + hci_ps_cmd[3] = opcode; + hci_ps_cmd[4] = (index & 0xFF); + hci_ps_cmd[5] = ((index >> 8) & 0xFF); + hci_ps_cmd[6] = length; + +} + +/* + *Create PS download commands from parsed data + */ +static int ath_create_ps_command(uint8_t opcode, + uint32_t param_1, + struct ps_cmd_packet *ps_patch_packet, + uint32_t *index) +{ + uint8_t *hci_ps_cmd; + int i; + + switch (opcode) { + + case WRITE_PATCH: + + for (i = 0; i < param_1; i++) { + + /* Allocate command buffer */ + hci_ps_cmd = (uint8_t *) malloc(ram_patch[i].Len + + HCI_CMD_HEADER_LEN); + + if (!hci_ps_cmd) + return -ENOMEM; + + /* Update commands to buffer */ + load_hci_header(hci_ps_cmd, + opcode, + ram_patch[i].Len, + i); + memcpy(&hci_ps_cmd[HCI_CMD_HEADER_LEN], + ram_patch[i].Data, + ram_patch[i].Len); + + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; + ps_patch_packet[*index].packetLen = ram_patch[i].Len + + HCI_CMD_HEADER_LEN; + + (*index)++; + } + + break; + case ENABLE_PATCH: + + hci_ps_cmd = (uint8_t *) malloc(HCI_CMD_HEADER_LEN); + + if (!hci_ps_cmd) + return -ENOMEM; + + load_hci_header(hci_ps_cmd, opcode, 0, 0x00); + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; + ps_patch_packet[*index].packetLen = HCI_CMD_HEADER_LEN; + + (*index)++; + + break; + case PS_RESET: + + hci_ps_cmd = (uint8_t *) malloc(PS_RESET_CMD_LEN); + + if (!hci_ps_cmd) + return -ENOMEM; + + load_hci_header(hci_ps_cmd, opcode, PS_RESET_PARAM_LEN, 0x00); + + hci_ps_cmd[7] = 0x00; + hci_ps_cmd[PS_RESET_CMD_LEN - 2] = (param_1 & 0xFF); + hci_ps_cmd[PS_RESET_CMD_LEN - 1] = ((param_1 >> 8) & 0xFF); + + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; + ps_patch_packet[*index].packetLen = PS_RESET_CMD_LEN; + + (*index)++; + + break; + case PS_WRITE: + + for (i = 0; i < param_1; i++) { + + hci_ps_cmd = + (uint8_t *) malloc(ps_tag_entry[i].tag_len + + HCI_CMD_HEADER_LEN); + if (!hci_ps_cmd) + return -ENOMEM; + + load_hci_header(hci_ps_cmd, + opcode, + ps_tag_entry[i].tag_len, + ps_tag_entry[i].tag_id); + + memcpy(&hci_ps_cmd[HCI_CMD_HEADER_LEN], + ps_tag_entry[i].tag_data, + ps_tag_entry[i].tag_len); + + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; + + ps_patch_packet[*index].packetLen = + ps_tag_entry[i].tag_len + HCI_CMD_HEADER_LEN; + + (*index)++; + } + + break; + default: + break; + } + + return 0; +} + +static int get_ps_type(char *line, + int eol_index, + unsigned char *type, + unsigned char *sub_type) +{ + + switch (eol_index) { + case 1: + return 0; + case 2: + (*type) = toupper(line[1]); + break; + case 3: + if (line[2] == ':') + (*type) = toupper(line[1]); + else if (line[1] == ':') + (*sub_type) = toupper(line[2]); + else + return -1; + + break; + case 4: + if (line[2] != ':') + return -1; + + (*type) = toupper(line[1]); + (*sub_type) = toupper(line[3]); + + break; + case -1: + return -1; + } + + return 0; +} + +static int get_input_data_format(char *line, struct ps_data_format *pst_format) +{ + unsigned char type = '\0'; + unsigned char sub_type = '\0'; + int eol_index = -1; + int i; + + /* The default values */ + set_pst_format(pst_format, PS_TYPE_HEX, 1); + + if (line[0] != '[') { + set_pst_format(pst_format, PS_TYPE_HEX, 1); + return 0; + } + + for (i = 1; i < 5; i++) { + if (line[i] == ']') { + eol_index = i; + break; + } + } + + if (get_ps_type(line, eol_index, &type, &sub_type) < 0) + return -1; + + /* By default Hex array type is assumed */ + if (type == '\0' && sub_type == '\0') + set_pst_format(pst_format, PS_TYPE_HEX, 1); + + /* Check is data type is of array */ + if (type == 'A' || sub_type == 'A') + pst_format->is_array = 1; + + if (type == 'S' || sub_type == 'S') + pst_format->is_array = 0; + + switch (type) { + + case 'D': + case 'B': + + pst_format->data_type = PS_TYPE_DEC; + break; + default: + + pst_format->data_type = PS_TYPE_HEX; + break; + + } + + line += (eol_index + 1); + + return 0; + +} + +static unsigned int read_data_in_section(char *line, + struct ps_data_format format_info) +{ + char *token_ptr = line; + + if (!token_ptr) + return 0x0FFF; + + if (token_ptr[0] == '[') { + + while (token_ptr[0] != ']' && token_ptr[0] != '\0') + token_ptr++; + + if (token_ptr[0] == '\0') + return 0x0FFF; + + token_ptr++; + } + + if (format_info.data_type == PS_TYPE_HEX) { + + if (format_info.is_array == 1) + return 0x0FFF; + else + return strtol(token_ptr, NULL, 16); + + } else + return 0x0FFF; + + return 0x0FFF; + +} +static int ath_parse_file(FILE *stream) +{ + char *buffer; + char *line; + uint8_t tag_cnt = 0; + int16_t byte_count = 0; + int read_count; + int num_ps_entry; + struct ps_data_format stps_data_format; + struct st_read_status read_status = { 0, 0, 0, 0 }; + + if (!stream) { + perror("Could not open config file .\n"); + return -1; + } + + buffer = malloc(LINE_SIZE_MAX + 1); + + if (!buffer) + return -ENOMEM; + + do { + line = fgets(buffer, LINE_SIZE_MAX, stream); + + if (!line) + break; + + skip_space(line); + + if ((line[0] == '/') && (line[1] == '/')) + continue; + + if ((line[0] == '#')) { + + if (read_status.section != 0) { + + if (buffer) + free(buffer); + return -1; + + } else { + read_status.section = 1; + continue; + } + } + + if ((line[0] == '/') && (line[1] == '*')) { + + read_status.section = 0; + continue; + } + + if (read_status.section == 1) { + skip_space(line); + + if (get_input_data_format( + line, &stps_data_format) < 0) { + + if (buffer) + free(buffer); + return -1; + + } + + ps_tag_entry[tag_cnt].tag_id = read_data_in_section( + line, + stps_data_format); + read_status.section = 2; + + } else if (read_status.section == 2) { + + if (get_input_data_format( + line, &stps_data_format) < 0) { + + if (buffer) + free(buffer); + return -1; + } + + byte_count = + read_data_in_section(line, stps_data_format); + + read_status.section = 2; + if (byte_count > LINE_SIZE_MAX / 2) { + if (buffer) + free(buffer); + + return -1; + } + + ps_tag_entry[tag_cnt].tag_len = byte_count; + ps_tag_entry[tag_cnt].tag_data = (uint8_t *) + malloc(byte_count); + + read_status.section = 3; + read_status.line_count = 0; + + } else if (read_status.section == 3) { + + if (read_status.line_count == 0) { + if (get_input_data_format( + line, &stps_data_format) < 0) { + if (buffer) + free(buffer); + return -1; + } + } + + skip_space(line); + read_status.char_cnt = 0; + + if (line[read_status.char_cnt] == '[') { + + while (line[read_status.char_cnt] != ']' && + line[read_status.char_cnt] != '\0') + read_status.char_cnt++; + + if (line[read_status.char_cnt] == ']') + read_status.char_cnt++; + else + read_status.char_cnt = 0; + + } + + read_count = (byte_count > BYTES_OF_PS_DATA_PER_LINE) + ? BYTES_OF_PS_DATA_PER_LINE : byte_count; + + if (stps_data_format.data_type == PS_TYPE_HEX && + stps_data_format.is_array == 1) { + + while (read_count > 0) { + + ps_tag_entry[tag_cnt].tag_data + [read_status.byte_count] = + stringtohex( + &line[read_status.char_cnt]); + + ps_tag_entry[tag_cnt].tag_data + [read_status.byte_count + 1] = + stringtohex( + &line[read_status.char_cnt + 3]); + + read_status.char_cnt += 6; + read_status.byte_count += 2; + read_count -= 2; + + } + + if (byte_count > BYTES_OF_PS_DATA_PER_LINE) + byte_count -= + BYTES_OF_PS_DATA_PER_LINE; + else + byte_count = 0; + } + + read_status.line_count++; + + if (byte_count == 0) { + read_status.section = 0; + read_status.char_cnt = 0; + read_status.line_count = 0; + read_status.byte_count = 0; + } else + read_status.char_cnt = 0; + + if (read_status.section == 0) { + tag_cnt++; + + if (tag_cnt == RAMPS_MAX_PS_TAGS_PER_FILE) { + if (buffer) + free(buffer); + return -1; + } + } + + } + + } while (line); + + num_ps_entry = tag_cnt; + + if (tag_cnt > RAMPS_MAX_PS_TAGS_PER_FILE) { + if (buffer) + free(buffer); + return -1; + } + + if (buffer) + free(buffer); + + return num_ps_entry; +} + +static int parse_patch_file(FILE *stream) +{ + char byte[3]; + char line[MAX_BYTE_LENGTH + 1]; + int byte_cnt, byte_cnt_org; + int patch_index; + int i, k; + int data; + int patch_count = 0; + + byte[2] = '\0'; + + while (fgets(line, MAX_BYTE_LENGTH, stream)) { + if (strlen(line) <= 1 || !isxdigit(line[0])) + continue; + else + break; + } + + byte_cnt = strtol(line, NULL, 16); + byte_cnt_org = byte_cnt; + + while (byte_cnt > MAX_BYTE_LENGTH) { + + /* Handle case when the number of patch buffer is + * more than the 20K */ + if (MAX_NUM_PATCH_ENTRY == patch_count) { + for (i = 0; i < patch_count; i++) + free(ram_patch[i].Data); + return -ENOMEM; + } + + ram_patch[patch_count].Len = MAX_BYTE_LENGTH; + ram_patch[patch_count].Data = + (uint8_t *) malloc(MAX_BYTE_LENGTH); + + if (!ram_patch[patch_count].Data) + return -ENOMEM; + + patch_count++; + byte_cnt = byte_cnt - MAX_BYTE_LENGTH; + } + + ram_patch[patch_count].Len = (byte_cnt & 0xFF); + + if (byte_cnt != 0) { + ram_patch[patch_count].Data = (uint8_t *) malloc(byte_cnt); + + if (!ram_patch[patch_count].Data) + return -ENOMEM; + patch_count++; + } + + while (byte_cnt_org > MAX_BYTE_LENGTH) { + + k = 0; + for (i = 0; i < MAX_BYTE_LENGTH * 2; i += 2) { + if (!fgets(byte, 3, stream)) + return -1; + data = strtoul(&byte[0], NULL, 16); + ram_patch[patch_index].Data[k] = (data & 0xFF); + + k++; + } + + patch_index++; + + byte_cnt_org = byte_cnt_org - MAX_BYTE_LENGTH; + } + + if (patch_index == 0) + patch_index++; + + for (k = 0; k < byte_cnt_org; k++) { + + if (!fgets(byte, 3, stream)) + return -1; + + data = strtoul(byte, NULL, 16); + ram_patch[patch_index].Data[k] = (data & 0xFF); + } + + return patch_count; +} + +static int ath_parse_ps(FILE *stream, int *total_tag_len) +{ + int num_ps_tags; + int i; + unsigned char bdaddr_present = 0; + + if (stream) + num_ps_tags = ath_parse_file(stream); + + if (num_ps_tags < 0) + return -1; + + if (num_ps_tags == 0) + *total_tag_len = 10; + else { + + for (i = 0; i < num_ps_tags; i++) { + + if (ps_tag_entry[i].tag_id == 1) + bdaddr_present = 1; + if (ps_tag_entry[i].tag_len % 2 == 1) + *total_tag_len = *total_tag_len + + ps_tag_entry[i].tag_len + 1; + else + *total_tag_len = + *total_tag_len + ps_tag_entry[i].tag_len; + + } + } + + if (num_ps_tags > 0 && !bdaddr_present) + *total_tag_len = *total_tag_len + 10; + + *total_tag_len = *total_tag_len + 10 + (num_ps_tags * 4); + + return num_ps_tags; +} + +static int ath_create_cmd_list(struct ps_cmd_packet **hci_packet_list, + uint32_t *num_packets, + int tag_count, + int patch_count, + int total_tag_len) +{ + uint8_t count; + uint32_t num_cmd_entry = 0; + + *num_packets = 0; + + if (patch_count || tag_count) { + + /* PS Reset Packet + Patch List + PS List */ + num_cmd_entry += (1 + patch_count + tag_count); + if (patch_count > 0) + num_cmd_entry++; /* Patch Enable Command */ + + (*hci_packet_list) = + malloc(sizeof(struct ps_cmd_packet) * num_cmd_entry); + + if (!(*hci_packet_list)) + return -ENOMEM; + + if (patch_count > 0) { + + if (ath_create_ps_command(WRITE_PATCH, patch_count, + *hci_packet_list, num_packets) < 0) + return -1; + if (ath_create_ps_command(ENABLE_PATCH, 0, + *hci_packet_list, num_packets) < 0) + return -1; + if (ath_create_ps_command(PS_RESET, + total_tag_len + EXTRA_PATCH_SIZE, + *hci_packet_list, num_packets) < 0) + return -1; + + } else { + + if (ath_create_ps_command(PS_RESET, total_tag_len, + *hci_packet_list, num_packets) < 0) + return -1; + } + + if (tag_count > 0) + ath_create_ps_command(PS_WRITE, tag_count, + *hci_packet_list, num_packets); + } + + for (count = 0; count < patch_count; count++) + free(ram_patch[patch_count].Data); + + for (count = 0; count < tag_count; count++) + free(ps_tag_entry[count].tag_data); + + return *num_packets; +} + +static int ath_free_command_list(struct ps_cmd_packet **hci_packet_list, + uint32_t num_packets) +{ + int i; + + if (!(*hci_packet_list)) + return -1; + + for (i = 0; i < num_packets; i++) + free((*hci_packet_list)[i].Hcipacket); + + free(*hci_packet_list); + + return 0; +} + +/* + * This API is used to send the HCI command to controller and return + * with a HCI Command Complete event. + */ +static int send_hci_cmd_wait_event(int dev, + uint8_t *hci_command, + int cmd_length, + uint8_t **event, uint8_t **buffer_to_free) +{ + int r; + uint8_t *hci_event; + uint8_t pkt_type = 0x01; + + if (cmd_length == 0) + return -1; + + if (write(dev, &pkt_type, 1) != 1) + return -1; + + if (write(dev, (unsigned char *)hci_command, cmd_length) != cmd_length) + return -1; + + hci_event = (uint8_t *) malloc(100); + + if (!hci_event) + return -ENOMEM; + + r = read_hci_event(dev, (unsigned char *)hci_event, 100); + + if (r > 0) { + *event = hci_event; + *buffer_to_free = hci_event; + } else { + + /* Did not get an event from controller. return error */ + free(hci_event); + *buffer_to_free = NULL; + return -1; + } + + return 0; +} + +static int read_ps_event(uint8_t *data) +{ + + if (data[5] == 0xFC && data[6] == 0x00) { + + switch (data[4]) { + + case 0x0B:/* CRC Check */ + case 0x0C:/* Change Baudrate */ + case 0x04:/* Enable sleep */ + return 0; + break; + default: + return -1; + break; + + } + } + + return -1; +} + +static int get_ps_file_name(int devtype, int rom_version, char *path) +{ + char *filename; + int status = 0; + + if (devtype == 0xdeadc0de) { + filename = PS_ASIC_FILE; + status = 1; + } else { + filename = PS_FPGA_FILE; + status = 0; + } + + sprintf(path, "%s%x/%s", FW_PATH, rom_version, filename); + + return status; +} + +static int get_patch_file_name(int dev_type, int rom_version, + int build_version, char *path) +{ + + if ((dev_type != 0) && + (dev_type != 0xdeadc0de) && + (rom_version == 0x99999999) && + (build_version == 1)) + path[0] = '\0'; + else + sprintf(path, "%s%x/%s", FW_PATH, rom_version, PATCH_FILE); + + return 0; +} +static int get_ar3k_crc(int dev, int tag_count, int patch_count) +{ + uint8_t hci_cmd[7]; + uint8_t *event; + uint8_t *buffer_to_free = NULL; + int retval = 1; + int crc; + + if (patch_count > 0) + crc |= RAM_PATCH_REGION; + if (tag_count > 0) + crc |= RAM_PS_REGION; + + load_hci_header(hci_cmd, PS_VERIFY_CRC, 0, crc); + + if (send_hci_cmd_wait_event(dev, + hci_cmd, + sizeof(hci_cmd), + &event, + &buffer_to_free) == 0) { + + if (read_ps_event(event) == 0) + retval = -1; + + if (buffer_to_free != NULL) + free(buffer_to_free); + } + + return retval; +} +static int get_device_type(int dev, uint32_t *code) +{ + uint8_t hci_cmd[] = { 0x05, 0xfc, 0x05, 0x00, 0x00, 0x00, 0x00, 0x04 }; + uint8_t *event; + uint8_t *buffer_to_free = NULL; + uint32_t reg; + int result = -1; + + *code = 0; + + hci_cmd[3] = (uint8_t) (DEV_REGISTER & 0xFF); + hci_cmd[4] = (uint8_t) ((DEV_REGISTER >> 8) & 0xFF); + hci_cmd[5] = (uint8_t) ((DEV_REGISTER >> 16) & 0xFF); + hci_cmd[6] = (uint8_t) ((DEV_REGISTER >> 24) & 0xFF); + + if (send_hci_cmd_wait_event(dev, + hci_cmd, + sizeof(hci_cmd), + &event, + &buffer_to_free) == 0) { + + if (event[5] == 0xFC && event[6] == 0x00) { + + switch (event[4]) { + case 0x05: + reg = event[10]; + reg = ((reg << 8) | event[9]); + reg = ((reg << 8) | event[8]); + reg = ((reg << 8) | event[7]); + *code = reg; + result = 0; + + break; + + case 0x06: + break; + } + } + } + + if (buffer_to_free) + free(buffer_to_free); + + return result; +} + +static int read_ar3k_version(int pConfig, int *rom_version, int *build_version) +{ + uint8_t hci_cmd[] = { 0x1E, 0xfc, 0x00 }; + uint8_t *event; + uint8_t *buffer_to_free = NULL; + int result = -1; + + if (send_hci_cmd_wait_event(pConfig, + hci_cmd, + sizeof(hci_cmd), + &event, + &buffer_to_free) == 0) { + + if (event[5] == 0xFC && + event[6] == 0x00 && + event[4] == 0x1E) { + + (*rom_version) = event[10]; + (*rom_version) = (((*rom_version) << 8) | event[9]); + (*rom_version) = (((*rom_version) << 8) | event[8]); + (*rom_version) = (((*rom_version) << 8) | event[7]); + + (*build_version) = event[14]; + (*build_version) = (((*build_version) << 8) | + event[13]); + (*build_version) = (((*build_version) << 8) | + event[12]); + (*build_version) = (((*build_version) << 8) | + event[11]); + + result = 1; + + } + + if (buffer_to_free) + free(buffer_to_free); + } + + return result; +} + +static int str2bdaddr(char *str_bdaddr, char *bdaddr) +{ + char bdbyte[3]; + char *str_byte = str_bdaddr; + int i, j; + unsigned char colon_present = 0; + + if (strstr(str_bdaddr, ":")) + colon_present = 1; + + bdbyte[2] = '\0'; + + for (i = 0, j = 5; i < 6; i++, j--) { + + bdbyte[0] = str_byte[0]; + bdbyte[1] = str_byte[1]; + bdaddr[j] = strtol(bdbyte, NULL, 16); + + if (colon_present == 1) + str_byte += 3; + else + str_byte += 2; + } + return 0; +} + +static int write_bdaddr(int pConfig, char *bdaddr) +{ + uint8_t bdaddr_cmd[] = { 0x0B, 0xFC, 0x0A, + 0x01, 0x01, 0x00, + 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x00 }; + uint8_t *event; + uint8_t *buffer_to_free = NULL; + int result = -1; + + str2bdaddr(bdaddr, (char *)&bdaddr_cmd[7]); + + if (send_hci_cmd_wait_event(pConfig, + bdaddr_cmd, + sizeof(bdaddr_cmd), + &event, + &buffer_to_free) == 0) { + + if (event[5] == 0xFC && event[6] == 0x00) { + if (event[4] == 0x0B) + result = 0; + } + + } + + if (buffer_to_free) + free(buffer_to_free); + + return result; +} + +int ath_configure_sleep(int fd, int sleep_stat) +{ + int dev_id, dd; + struct timespec tm = {0, 50000}; + + dev_id = ioctl(fd, HCIUARTGETDEVICE, 0); + + if (dev_id < 0) { + perror("cannot get device id"); + return -1; + } + + dd = hci_open_dev(dev_id); + + if (dd < 0) { + perror("HCI device open failed"); + return -1; + } + + sleep(2); + + /* send vendor specific command with Sleep feature Enabled */ + if (hci_send_cmd(dd, OGF_VENDOR_CMD, 0x04, 1, &sleep_stat) < 0) + perror("Power management Disabled"); + + nanosleep(&tm, NULL); + hci_close_dev(dd); + + return 0; +} + +int ath_ps_download(int hdev) +{ + int i; + int status = 0; + int tag_count; + int patch_count; + int total_tag_len = 0; + int rom_version = 0, build_version = 0; + + struct ps_cmd_packet *hci_cmd_list; /* List storing the commands */ + uint32_t num_cmds; + uint8_t *event; + uint8_t *buffer_to_free; + uint32_t dev_type; + + char patch_file[PATH_MAX]; + char ps_file[PATH_MAX]; + char bdaddr_file[PATH_MAX]; + + FILE *stream; + char bdaddr[21]; + + hci_cmd_list = NULL; + + /* + * Verfiy firmware version. depending on it select the PS + * config file to download. + */ + if (get_device_type(hdev, &dev_type) == -1) { + status = -1; + goto download_cmplete; + } + if (read_ar3k_version(hdev, &rom_version, &build_version) == -1) { + status = -1; + goto download_cmplete; + } + + get_ps_file_name(dev_type, rom_version, ps_file); + + get_patch_file_name(dev_type, rom_version, build_version, patch_file); + + /* Read the PS file to a dynamically allocated buffer */ + stream = fopen(ps_file, "r"); + + if (!stream) { + perror("firmware file open error\n"); + status = -1; + goto download_cmplete; + } + + tag_count = ath_parse_ps(stream, &total_tag_len); + + fclose(stream); + + if (tag_count == -1) { + status = -1; + goto download_cmplete; + } + + /* + * It is not necessary that Patch file be available, + * continue with PS Operations if. + * failed. + */ + if (patch_file[0] == '\0') + status = 0; + + stream = fopen(patch_file, "r"); + + if (!stream) { + patch_count = 0; + status = 0; + } else { + /* parse and store the Patch file contents to + * a global variables + */ + patch_count = parse_patch_file(stream); + + fclose(stream); + + if (patch_count < 0) { + status = -1; + goto download_cmplete; + } + } + + /* + * Send the CRC packet, + * Continue with the PS operations + * only if the CRC check failed + */ + if (get_ar3k_crc(hdev, tag_count, patch_count) < 0) { + status = 0; + goto download_cmplete; + } + + /* Create an HCI command list + * from the parsed PS and patch information + */ + ath_create_cmd_list(&hci_cmd_list, + &num_cmds, + tag_count, + patch_count, + total_tag_len); + + for (i = 0; i < num_cmds; i++) { + + if (send_hci_cmd_wait_event + (hdev, + hci_cmd_list[i].Hcipacket, + hci_cmd_list[i].packetLen, + &event, + &buffer_to_free) == 0) { + + if (read_ps_event(event) < 0) { + + /* Exit if the status is not success */ + if (buffer_to_free) + free(buffer_to_free); + + status = -1; + goto download_cmplete; + } + if (buffer_to_free) + free(buffer_to_free); + } else { + status = 0; + goto download_cmplete; + } + } + /* Read the PS file to a dynamically allocated buffer */ + sprintf(bdaddr_file, "%s%x/%s", FW_PATH, rom_version, BDADDR_FILE); + + stream = fopen(bdaddr_file, "r"); + + if (!stream) { + status = 0; + goto download_cmplete; + } + + if (fgets(bdaddr, 20, stream)) + status = write_bdaddr(hdev, bdaddr); + + fclose(stream); + +download_cmplete: + + if (hci_cmd_list) + ath_free_command_list(&hci_cmd_list, num_cmds); + + return status; +} -- 1.7.0 ^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [PATCH v3] hciattach application support for Atheros AR300x Bluetooth Chip 2010-05-12 13:47 ` [PATCH v3] hciattach application support for " suraj @ 2010-05-20 13:37 ` suraj 2010-05-20 16:00 ` Marcel Holtmann 1 sibling, 0 replies; 36+ messages in thread From: suraj @ 2010-05-20 13:37 UTC (permalink / raw) To: linux-bluetooth; +Cc: marcel, Luis.Rodriguez, Jothikumar.Mothilal Hi, On Wed, 2010-05-12 at 19:17 +0530, suraj wrote: > Implements support for Atheros AR300x Bluetooth chip in hciattach. > Adds feature to bring up AR300x Bluetooth chip > with and without enhanced power management enabled. > > Signed-off-by: Suraj <suraj@Atheros.com> > > --- > Makefile.tools | 1 + > tools/hciattach.8 | 6 + > tools/hciattach.c | 111 +++++ > tools/hciattach.h | 3 + > tools/hciattach_ar3k.c | 1223 ++++++++++++++++++++++++++++++++++++++++++++++++ > 5 files changed, 1344 insertions(+), 0 deletions(-) > create mode 100755 tools/hciattach_ar3k.c > > diff --git a/Makefile.tools b/Makefile.tools > index 2735d68..48cf097 100644 > --- a/Makefile.tools > +++ b/Makefile.tools > @@ -23,6 +23,7 @@ tools_l2ping_LDADD = lib/libbluetooth.la > tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \ > tools/hciattach_st.c \ > tools/hciattach_ti.c \ > + tools/hciattach_ar3k.c \ > tools/hciattach_tialt.c > tools_hciattach_LDADD = lib/libbluetooth.la > > diff --git a/tools/hciattach.8 b/tools/hciattach.8 > index f750222..ef943ea 100644 > --- a/tools/hciattach.8 > +++ b/tools/hciattach.8 > @@ -49,6 +49,12 @@ specific identifier. Currently supported types are > .B any > Unspecified HCI_UART interface, no vendor specific options > .TP > +.B ar3kalt > +Atheros AR300x based serial bluetooth device with power management disabled > +.TP > +.B ar3k > +Atheros AR300x based serial bluetooth device > +.TP > .B ericsson > Ericsson based modules > .TP > diff --git a/tools/hciattach.c b/tools/hciattach.c > index b13db1b..768a3f1 100644 > --- a/tools/hciattach.c > +++ b/tools/hciattach.c > @@ -653,6 +653,110 @@ static int csr(int fd, struct uart_t *u, struct termios *ti) > } > > /* > + * Atheros AR300x specific initialization post callback > + * with power management dsiabled > + * Suraj Sumangala <Suraj@Atheros.com> > + */ > +static int ar3kpost(int fd, struct uart_t *u, struct termios *ti) > +{ > + return ath_configure_sleep(fd, 0); > +} > + > +/* > + * Atheros AR300x specific initialization post callback > + * with power management enabled > + * Suraj Sumangala <Suraj@Atheros.com> > + */ > +static int ar3kpmpost(int fd, struct uart_t *u, struct termios *ti) > +{ > + return ath_configure_sleep(fd, 1); > +} > +/* > + * Atheros AR300x specific initialization > + * Suraj Sumangala <Suraj@Atheros.com> > + */ > +static int ar3kinit(int fd, struct uart_t *u, struct termios *ti) > +{ > + struct timespec tm = { 0, 500000 }; > + unsigned char cmd[14], rsp[100]; > + int r; > + int baud; > + > + /* Download PS and patch */ > + r = ath_ps_download(fd); > + > + if (r < 0) { > + perror("Failed to Download configuration"); > + return -1; > + } > + > + /* Write BDADDR if user has provided any */ > + if (u->bdaddr != NULL) { > + /* Set BD_ADDR */ > + memset(cmd, 0, sizeof(cmd)); > + memset(rsp, 0, sizeof(rsp)); > + cmd[0] = HCI_COMMAND_PKT; > + cmd[1] = 0x0B; > + cmd[2] = 0xfc; > + cmd[3] = 0x0A; > + cmd[4] = 0x01; > + cmd[5] = 0x01; > + cmd[6] = 0x00; > + cmd[7] = 0x06; > + str2ba(u->bdaddr, (bdaddr_t *) (cmd + 8)); > + > + /* Send command */ > + if (write(fd, cmd, 14) != 14) { > + fprintf(stderr, "Failed to write BD_ADDR command\n"); > + return -1; > + } > + > + /* Read reply */ > + if (read_hci_event(fd, rsp, 10) < 0) { > + fprintf(stderr, "Failed to set BD_ADDR\n"); > + return -1; > + } > + } > + > + /* Send HCI Reset to write the configuration */ > + cmd[0] = HCI_COMMAND_PKT; > + cmd[1] = 0x03; > + cmd[2] = 0x0C; > + cmd[3] = 0x00; > + > + r = write(fd, cmd, 4); > + > + if (r != 4) > + return -1; > + > + nanosleep(&tm, NULL); > + if (read_hci_event(fd, rsp, sizeof(rsp)) < 0) > + return -1; > + > + /* Set baud rate command, > + * set controller baud rate to user specified value */ > + cmd[0] = HCI_COMMAND_PKT; > + cmd[1] = 0x0C; > + cmd[2] = 0xfc; > + cmd[3] = 0x02; > + baud = u->speed/100; > + cmd[4] = (char)baud; > + cmd[5] = (char)(baud >> 8); > + > + if (write(fd, cmd, 6) != 6) { > + perror("Failed to write init command"); > + return -1; > + } > + > + /* Wait for the command complete event for Baud rate change Command */ > + nanosleep(&tm, NULL); > + > + if (read_hci_event(fd, rsp, sizeof(rsp)) < 0) > + return -1; > + > + return 0; > +} > +/* > * Silicon Wave specific initialization > * Thomas Moser <thomas.moser@tmoser.ch> > */ > @@ -1071,6 +1175,13 @@ struct uart_t uart[] = { > /* Broadcom BCM2035 */ > { "bcm2035", 0x0A5C, 0x2035, HCI_UART_H4, 115200, 460800, FLOW_CTL, NULL, bcm2035 }, > > + /* ATHEROS AR300x */ > + { "ar3kalt", 0x0000, 0x0000, HCI_UART_ATH, 115200, 115200, > + FLOW_CTL, NULL, ar3kinit, ar3kpost }, > + > + { "ar3k", 0x0000, 0x0000, HCI_UART_ATH, 115200, 115200, > + FLOW_CTL, NULL, ar3kinit, ar3kpmpost }, > + > { NULL, 0 } > }; > > diff --git a/tools/hciattach.h b/tools/hciattach.h > index 867563b..7719e04 100644 > --- a/tools/hciattach.h > +++ b/tools/hciattach.h > @@ -36,6 +36,7 @@ > #define HCI_UART_3WIRE 2 > #define HCI_UART_H4DS 3 > #define HCI_UART_LL 4 > +#define HCI_UART_ATH 5 > > int read_hci_event(int fd, unsigned char* buf, int size); > int set_speed(int fd, struct termios *ti, int speed); > @@ -45,3 +46,5 @@ int texas_post(int fd, struct termios *ti); > int texasalt_init(int fd, int speed, struct termios *ti); > int stlc2500_init(int fd, bdaddr_t *bdaddr); > int bgb2xx_init(int dd, bdaddr_t *bdaddr); > +int ath_configure_sleep(int fd, int sleep_stat); > +int ath_ps_download(int fd); > diff --git a/tools/hciattach_ar3k.c b/tools/hciattach_ar3k.c > new file mode 100755 > index 0000000..2542161 > --- /dev/null > +++ b/tools/hciattach_ar3k.c > @@ -0,0 +1,1223 @@ > +/* > + * Copyright (c) 2009-2010 Atheros Communications Inc. > + * > + * 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 > + * > + */ > + > +#ifdef HAVE_CONFIG_H > +#include <config.h> > +#endif > + > +#include <stdio.h> > +#include <errno.h> > +#include <unistd.h> > +#include <stdlib.h> > +#include <ctype.h> > +#include <time.h> > +#include <sys/time.h> > +#include <sys/types.h> > +#include <sys/param.h> > +#include <sys/ioctl.h> > + > +#include <bluetooth/bluetooth.h> > +#include <bluetooth/hci.h> > +#include <bluetooth/hci_lib.h> > + > +#include "hciattach.h" > + > +/* The maximum number of bytes possible in a patch entry */ > +#define MAX_PATCH_SIZE 20000 > + > +#define MAX_BYTE_LENGTH 244 > + > +/* Maximum HCI packets that will be formed from the Patch file */ > +#define MAX_NUM_PATCH_ENTRY ((MAX_PATCH_SIZE/MAX_BYTE_LENGTH) + 1) > + > +#define DEV_REGISTER 0x4FFC > + > +#define FW_PATH "/lib/firmware/ar3k/" > + > +#define PS_ASIC_FILE "PS_ASIC.pst" > +#define PS_FPGA_FILE "PS_FPGA.pst" > +#define PATCH_FILE "RamPatch.txt" > +#define BDADDR_FILE "ar3kbdaddr.pst" > + > +#define HCI_CMD_HEADER_LEN 7 > + > +/* PS command types */ > +#define PS_RESET 2 > +#define PS_WRITE 1 > +#define WRITE_PATCH 8 > +#define PS_VERIFY_CRC 9 > +#define ENABLE_PATCH 11 > + > +#define EXTRA_PATCH_SIZE 500 > + > +/* PS configuration entry time */ > +#define PS_TYPE_HEX 0 > +#define PS_TYPE_DEC 1 > + > +#define PS_RESET_PARAM_LEN 6 > +#define PS_RESET_CMD_LEN (PS_RESET_PARAM_LEN +\ > + HCI_CMD_HEADER_LEN) > + > +#define RAM_PS_REGION (1<<0) > +#define RAM_PATCH_REGION (1<<1) > + > +#define RAMPS_MAX_PS_TAGS_PER_FILE 50 > +#define PS_MAX_LEN 500 > +#define LINE_SIZE_MAX (PS_MAX_LEN * 2) > + > +#define BYTES_OF_PS_DATA_PER_LINE 16 > + > +#define skip_space(str) while (*(str) == (' ')) ((str)++) > + > +#define is_between(x, lower, upper) (((lower) <= (x)) && ((x) <= (upper))) > + > +#define tohexval(c) (isdigit((c)) ? ((c) - '0') : \ > + (is_between((c), 'A', 'Z') ? \ > + ((c) - 'A' + 10) : ((c) - 'a' + 10))) > + > +#define stringtohex(str) (((uint8_t)(tohexval((str)[0]) << 4)) |\ > + ((uint8_t)tohexval((str)[1]))) > + > +#define set_pst_format(pst, type, array_val) ((pst)->data_type = (type),\ > + (pst)->is_array = (array_val)) > + > +struct ps_tag_entry { > + uint32_t tag_id; > + uint32_t tag_len; > + uint8_t *tag_data; > +}; > + > +struct ps_ram_patch { > + int16_t Len; > + uint8_t *Data; > +}; > + > +struct ps_data_format { > + unsigned char data_type; > + unsigned char is_array; > +}; > + > +struct ps_cmd_packet { > + uint8_t *Hcipacket; > + int packetLen; > +}; > + > +struct st_read_status { > + unsigned section; > + unsigned line_count; > + unsigned char_cnt; > + unsigned byte_count; > +}; > + > +struct ps_tag_entry ps_tag_entry[RAMPS_MAX_PS_TAGS_PER_FILE]; > +struct ps_ram_patch ram_patch[MAX_NUM_PATCH_ENTRY]; > + > +static void load_hci_header(uint8_t *hci_ps_cmd, > + uint8_t opcode, > + int length, > + int index) > +{ > + hci_ps_cmd[0] = 0x0B; > + hci_ps_cmd[1] = 0xFC; > + hci_ps_cmd[2] = length + 4; > + hci_ps_cmd[3] = opcode; > + hci_ps_cmd[4] = (index & 0xFF); > + hci_ps_cmd[5] = ((index >> 8) & 0xFF); > + hci_ps_cmd[6] = length; > + > +} > + > +/* > + *Create PS download commands from parsed data > + */ > +static int ath_create_ps_command(uint8_t opcode, > + uint32_t param_1, > + struct ps_cmd_packet *ps_patch_packet, > + uint32_t *index) > +{ > + uint8_t *hci_ps_cmd; > + int i; > + > + switch (opcode) { > + > + case WRITE_PATCH: > + > + for (i = 0; i < param_1; i++) { > + > + /* Allocate command buffer */ > + hci_ps_cmd = (uint8_t *) malloc(ram_patch[i].Len + > + HCI_CMD_HEADER_LEN); > + > + if (!hci_ps_cmd) > + return -ENOMEM; > + > + /* Update commands to buffer */ > + load_hci_header(hci_ps_cmd, > + opcode, > + ram_patch[i].Len, > + i); > + memcpy(&hci_ps_cmd[HCI_CMD_HEADER_LEN], > + ram_patch[i].Data, > + ram_patch[i].Len); > + > + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; > + ps_patch_packet[*index].packetLen = ram_patch[i].Len + > + HCI_CMD_HEADER_LEN; > + > + (*index)++; > + } > + > + break; > + case ENABLE_PATCH: > + > + hci_ps_cmd = (uint8_t *) malloc(HCI_CMD_HEADER_LEN); > + > + if (!hci_ps_cmd) > + return -ENOMEM; > + > + load_hci_header(hci_ps_cmd, opcode, 0, 0x00); > + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; > + ps_patch_packet[*index].packetLen = HCI_CMD_HEADER_LEN; > + > + (*index)++; > + > + break; > + case PS_RESET: > + > + hci_ps_cmd = (uint8_t *) malloc(PS_RESET_CMD_LEN); > + > + if (!hci_ps_cmd) > + return -ENOMEM; > + > + load_hci_header(hci_ps_cmd, opcode, PS_RESET_PARAM_LEN, 0x00); > + > + hci_ps_cmd[7] = 0x00; > + hci_ps_cmd[PS_RESET_CMD_LEN - 2] = (param_1 & 0xFF); > + hci_ps_cmd[PS_RESET_CMD_LEN - 1] = ((param_1 >> 8) & 0xFF); > + > + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; > + ps_patch_packet[*index].packetLen = PS_RESET_CMD_LEN; > + > + (*index)++; > + > + break; > + case PS_WRITE: > + > + for (i = 0; i < param_1; i++) { > + > + hci_ps_cmd = > + (uint8_t *) malloc(ps_tag_entry[i].tag_len + > + HCI_CMD_HEADER_LEN); > + if (!hci_ps_cmd) > + return -ENOMEM; > + > + load_hci_header(hci_ps_cmd, > + opcode, > + ps_tag_entry[i].tag_len, > + ps_tag_entry[i].tag_id); > + > + memcpy(&hci_ps_cmd[HCI_CMD_HEADER_LEN], > + ps_tag_entry[i].tag_data, > + ps_tag_entry[i].tag_len); > + > + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; > + > + ps_patch_packet[*index].packetLen = > + ps_tag_entry[i].tag_len + HCI_CMD_HEADER_LEN; > + > + (*index)++; > + } > + > + break; > + default: > + break; > + } > + > + return 0; > +} > + > +static int get_ps_type(char *line, > + int eol_index, > + unsigned char *type, > + unsigned char *sub_type) > +{ > + > + switch (eol_index) { > + case 1: > + return 0; > + case 2: > + (*type) = toupper(line[1]); > + break; > + case 3: > + if (line[2] == ':') > + (*type) = toupper(line[1]); > + else if (line[1] == ':') > + (*sub_type) = toupper(line[2]); > + else > + return -1; > + > + break; > + case 4: > + if (line[2] != ':') > + return -1; > + > + (*type) = toupper(line[1]); > + (*sub_type) = toupper(line[3]); > + > + break; > + case -1: > + return -1; > + } > + > + return 0; > +} > + > +static int get_input_data_format(char *line, struct ps_data_format *pst_format) > +{ > + unsigned char type = '\0'; > + unsigned char sub_type = '\0'; > + int eol_index = -1; > + int i; > + > + /* The default values */ > + set_pst_format(pst_format, PS_TYPE_HEX, 1); > + > + if (line[0] != '[') { > + set_pst_format(pst_format, PS_TYPE_HEX, 1); > + return 0; > + } > + > + for (i = 1; i < 5; i++) { > + if (line[i] == ']') { > + eol_index = i; > + break; > + } > + } > + > + if (get_ps_type(line, eol_index, &type, &sub_type) < 0) > + return -1; > + > + /* By default Hex array type is assumed */ > + if (type == '\0' && sub_type == '\0') > + set_pst_format(pst_format, PS_TYPE_HEX, 1); > + > + /* Check is data type is of array */ > + if (type == 'A' || sub_type == 'A') > + pst_format->is_array = 1; > + > + if (type == 'S' || sub_type == 'S') > + pst_format->is_array = 0; > + > + switch (type) { > + > + case 'D': > + case 'B': > + > + pst_format->data_type = PS_TYPE_DEC; > + break; > + default: > + > + pst_format->data_type = PS_TYPE_HEX; > + break; > + > + } > + > + line += (eol_index + 1); > + > + return 0; > + > +} > + > +static unsigned int read_data_in_section(char *line, > + struct ps_data_format format_info) > +{ > + char *token_ptr = line; > + > + if (!token_ptr) > + return 0x0FFF; > + > + if (token_ptr[0] == '[') { > + > + while (token_ptr[0] != ']' && token_ptr[0] != '\0') > + token_ptr++; > + > + if (token_ptr[0] == '\0') > + return 0x0FFF; > + > + token_ptr++; > + } > + > + if (format_info.data_type == PS_TYPE_HEX) { > + > + if (format_info.is_array == 1) > + return 0x0FFF; > + else > + return strtol(token_ptr, NULL, 16); > + > + } else > + return 0x0FFF; > + > + return 0x0FFF; > + > +} > +static int ath_parse_file(FILE *stream) > +{ > + char *buffer; > + char *line; > + uint8_t tag_cnt = 0; > + int16_t byte_count = 0; > + int read_count; > + int num_ps_entry; > + struct ps_data_format stps_data_format; > + struct st_read_status read_status = { 0, 0, 0, 0 }; > + > + if (!stream) { > + perror("Could not open config file .\n"); > + return -1; > + } > + > + buffer = malloc(LINE_SIZE_MAX + 1); > + > + if (!buffer) > + return -ENOMEM; > + > + do { > + line = fgets(buffer, LINE_SIZE_MAX, stream); > + > + if (!line) > + break; > + > + skip_space(line); > + > + if ((line[0] == '/') && (line[1] == '/')) > + continue; > + > + if ((line[0] == '#')) { > + > + if (read_status.section != 0) { > + > + if (buffer) > + free(buffer); > + return -1; > + > + } else { > + read_status.section = 1; > + continue; > + } > + } > + > + if ((line[0] == '/') && (line[1] == '*')) { > + > + read_status.section = 0; > + continue; > + } > + > + if (read_status.section == 1) { > + skip_space(line); > + > + if (get_input_data_format( > + line, &stps_data_format) < 0) { > + > + if (buffer) > + free(buffer); > + return -1; > + > + } > + > + ps_tag_entry[tag_cnt].tag_id = read_data_in_section( > + line, > + stps_data_format); > + read_status.section = 2; > + > + } else if (read_status.section == 2) { > + > + if (get_input_data_format( > + line, &stps_data_format) < 0) { > + > + if (buffer) > + free(buffer); > + return -1; > + } > + > + byte_count = > + read_data_in_section(line, stps_data_format); > + > + read_status.section = 2; > + if (byte_count > LINE_SIZE_MAX / 2) { > + if (buffer) > + free(buffer); > + > + return -1; > + } > + > + ps_tag_entry[tag_cnt].tag_len = byte_count; > + ps_tag_entry[tag_cnt].tag_data = (uint8_t *) > + malloc(byte_count); > + > + read_status.section = 3; > + read_status.line_count = 0; > + > + } else if (read_status.section == 3) { > + > + if (read_status.line_count == 0) { > + if (get_input_data_format( > + line, &stps_data_format) < 0) { > + if (buffer) > + free(buffer); > + return -1; > + } > + } > + > + skip_space(line); > + read_status.char_cnt = 0; > + > + if (line[read_status.char_cnt] == '[') { > + > + while (line[read_status.char_cnt] != ']' && > + line[read_status.char_cnt] != '\0') > + read_status.char_cnt++; > + > + if (line[read_status.char_cnt] == ']') > + read_status.char_cnt++; > + else > + read_status.char_cnt = 0; > + > + } > + > + read_count = (byte_count > BYTES_OF_PS_DATA_PER_LINE) > + ? BYTES_OF_PS_DATA_PER_LINE : byte_count; > + > + if (stps_data_format.data_type == PS_TYPE_HEX && > + stps_data_format.is_array == 1) { > + > + while (read_count > 0) { > + > + ps_tag_entry[tag_cnt].tag_data > + [read_status.byte_count] = > + stringtohex( > + &line[read_status.char_cnt]); > + > + ps_tag_entry[tag_cnt].tag_data > + [read_status.byte_count + 1] = > + stringtohex( > + &line[read_status.char_cnt + 3]); > + > + read_status.char_cnt += 6; > + read_status.byte_count += 2; > + read_count -= 2; > + > + } > + > + if (byte_count > BYTES_OF_PS_DATA_PER_LINE) > + byte_count -= > + BYTES_OF_PS_DATA_PER_LINE; > + else > + byte_count = 0; > + } > + > + read_status.line_count++; > + > + if (byte_count == 0) { > + read_status.section = 0; > + read_status.char_cnt = 0; > + read_status.line_count = 0; > + read_status.byte_count = 0; > + } else > + read_status.char_cnt = 0; > + > + if (read_status.section == 0) { > + tag_cnt++; > + > + if (tag_cnt == RAMPS_MAX_PS_TAGS_PER_FILE) { > + if (buffer) > + free(buffer); > + return -1; > + } > + } > + > + } > + > + } while (line); > + > + num_ps_entry = tag_cnt; > + > + if (tag_cnt > RAMPS_MAX_PS_TAGS_PER_FILE) { > + if (buffer) > + free(buffer); > + return -1; > + } > + > + if (buffer) > + free(buffer); > + > + return num_ps_entry; > +} > + > +static int parse_patch_file(FILE *stream) > +{ > + char byte[3]; > + char line[MAX_BYTE_LENGTH + 1]; > + int byte_cnt, byte_cnt_org; > + int patch_index; > + int i, k; > + int data; > + int patch_count = 0; > + > + byte[2] = '\0'; > + > + while (fgets(line, MAX_BYTE_LENGTH, stream)) { > + if (strlen(line) <= 1 || !isxdigit(line[0])) > + continue; > + else > + break; > + } > + > + byte_cnt = strtol(line, NULL, 16); > + byte_cnt_org = byte_cnt; > + > + while (byte_cnt > MAX_BYTE_LENGTH) { > + > + /* Handle case when the number of patch buffer is > + * more than the 20K */ > + if (MAX_NUM_PATCH_ENTRY == patch_count) { > + for (i = 0; i < patch_count; i++) > + free(ram_patch[i].Data); > + return -ENOMEM; > + } > + > + ram_patch[patch_count].Len = MAX_BYTE_LENGTH; > + ram_patch[patch_count].Data = > + (uint8_t *) malloc(MAX_BYTE_LENGTH); > + > + if (!ram_patch[patch_count].Data) > + return -ENOMEM; > + > + patch_count++; > + byte_cnt = byte_cnt - MAX_BYTE_LENGTH; > + } > + > + ram_patch[patch_count].Len = (byte_cnt & 0xFF); > + > + if (byte_cnt != 0) { > + ram_patch[patch_count].Data = (uint8_t *) malloc(byte_cnt); > + > + if (!ram_patch[patch_count].Data) > + return -ENOMEM; > + patch_count++; > + } > + > + while (byte_cnt_org > MAX_BYTE_LENGTH) { > + > + k = 0; > + for (i = 0; i < MAX_BYTE_LENGTH * 2; i += 2) { > + if (!fgets(byte, 3, stream)) > + return -1; > + data = strtoul(&byte[0], NULL, 16); > + ram_patch[patch_index].Data[k] = (data & 0xFF); > + > + k++; > + } > + > + patch_index++; > + > + byte_cnt_org = byte_cnt_org - MAX_BYTE_LENGTH; > + } > + > + if (patch_index == 0) > + patch_index++; > + > + for (k = 0; k < byte_cnt_org; k++) { > + > + if (!fgets(byte, 3, stream)) > + return -1; > + > + data = strtoul(byte, NULL, 16); > + ram_patch[patch_index].Data[k] = (data & 0xFF); > + } > + > + return patch_count; > +} > + > +static int ath_parse_ps(FILE *stream, int *total_tag_len) > +{ > + int num_ps_tags; > + int i; > + unsigned char bdaddr_present = 0; > + > + if (stream) > + num_ps_tags = ath_parse_file(stream); > + > + if (num_ps_tags < 0) > + return -1; > + > + if (num_ps_tags == 0) > + *total_tag_len = 10; > + else { > + > + for (i = 0; i < num_ps_tags; i++) { > + > + if (ps_tag_entry[i].tag_id == 1) > + bdaddr_present = 1; > + if (ps_tag_entry[i].tag_len % 2 == 1) > + *total_tag_len = *total_tag_len > + + ps_tag_entry[i].tag_len + 1; > + else > + *total_tag_len = > + *total_tag_len + ps_tag_entry[i].tag_len; > + > + } > + } > + > + if (num_ps_tags > 0 && !bdaddr_present) > + *total_tag_len = *total_tag_len + 10; > + > + *total_tag_len = *total_tag_len + 10 + (num_ps_tags * 4); > + > + return num_ps_tags; > +} > + > +static int ath_create_cmd_list(struct ps_cmd_packet **hci_packet_list, > + uint32_t *num_packets, > + int tag_count, > + int patch_count, > + int total_tag_len) > +{ > + uint8_t count; > + uint32_t num_cmd_entry = 0; > + > + *num_packets = 0; > + > + if (patch_count || tag_count) { > + > + /* PS Reset Packet + Patch List + PS List */ > + num_cmd_entry += (1 + patch_count + tag_count); > + if (patch_count > 0) > + num_cmd_entry++; /* Patch Enable Command */ > + > + (*hci_packet_list) = > + malloc(sizeof(struct ps_cmd_packet) * num_cmd_entry); > + > + if (!(*hci_packet_list)) > + return -ENOMEM; > + > + if (patch_count > 0) { > + > + if (ath_create_ps_command(WRITE_PATCH, patch_count, > + *hci_packet_list, num_packets) < 0) > + return -1; > + if (ath_create_ps_command(ENABLE_PATCH, 0, > + *hci_packet_list, num_packets) < 0) > + return -1; > + if (ath_create_ps_command(PS_RESET, > + total_tag_len + EXTRA_PATCH_SIZE, > + *hci_packet_list, num_packets) < 0) > + return -1; > + > + } else { > + > + if (ath_create_ps_command(PS_RESET, total_tag_len, > + *hci_packet_list, num_packets) < 0) > + return -1; > + } > + > + if (tag_count > 0) > + ath_create_ps_command(PS_WRITE, tag_count, > + *hci_packet_list, num_packets); > + } > + > + for (count = 0; count < patch_count; count++) > + free(ram_patch[patch_count].Data); > + > + for (count = 0; count < tag_count; count++) > + free(ps_tag_entry[count].tag_data); > + > + return *num_packets; > +} > + > +static int ath_free_command_list(struct ps_cmd_packet **hci_packet_list, > + uint32_t num_packets) > +{ > + int i; > + > + if (!(*hci_packet_list)) > + return -1; > + > + for (i = 0; i < num_packets; i++) > + free((*hci_packet_list)[i].Hcipacket); > + > + free(*hci_packet_list); > + > + return 0; > +} > + > +/* > + * This API is used to send the HCI command to controller and return > + * with a HCI Command Complete event. > + */ > +static int send_hci_cmd_wait_event(int dev, > + uint8_t *hci_command, > + int cmd_length, > + uint8_t **event, uint8_t **buffer_to_free) > +{ > + int r; > + uint8_t *hci_event; > + uint8_t pkt_type = 0x01; > + > + if (cmd_length == 0) > + return -1; > + > + if (write(dev, &pkt_type, 1) != 1) > + return -1; > + > + if (write(dev, (unsigned char *)hci_command, cmd_length) != cmd_length) > + return -1; > + > + hci_event = (uint8_t *) malloc(100); > + > + if (!hci_event) > + return -ENOMEM; > + > + r = read_hci_event(dev, (unsigned char *)hci_event, 100); > + > + if (r > 0) { > + *event = hci_event; > + *buffer_to_free = hci_event; > + } else { > + > + /* Did not get an event from controller. return error */ > + free(hci_event); > + *buffer_to_free = NULL; > + return -1; > + } > + > + return 0; > +} > + > +static int read_ps_event(uint8_t *data) > +{ > + > + if (data[5] == 0xFC && data[6] == 0x00) { > + > + switch (data[4]) { > + > + case 0x0B:/* CRC Check */ > + case 0x0C:/* Change Baudrate */ > + case 0x04:/* Enable sleep */ > + return 0; > + break; > + default: > + return -1; > + break; > + > + } > + } > + > + return -1; > +} > + > +static int get_ps_file_name(int devtype, int rom_version, char *path) > +{ > + char *filename; > + int status = 0; > + > + if (devtype == 0xdeadc0de) { > + filename = PS_ASIC_FILE; > + status = 1; > + } else { > + filename = PS_FPGA_FILE; > + status = 0; > + } > + > + sprintf(path, "%s%x/%s", FW_PATH, rom_version, filename); > + > + return status; > +} > + > +static int get_patch_file_name(int dev_type, int rom_version, > + int build_version, char *path) > +{ > + > + if ((dev_type != 0) && > + (dev_type != 0xdeadc0de) && > + (rom_version == 0x99999999) && > + (build_version == 1)) > + path[0] = '\0'; > + else > + sprintf(path, "%s%x/%s", FW_PATH, rom_version, PATCH_FILE); > + > + return 0; > +} > +static int get_ar3k_crc(int dev, int tag_count, int patch_count) > +{ > + uint8_t hci_cmd[7]; > + uint8_t *event; > + uint8_t *buffer_to_free = NULL; > + int retval = 1; > + int crc; > + > + if (patch_count > 0) > + crc |= RAM_PATCH_REGION; > + if (tag_count > 0) > + crc |= RAM_PS_REGION; > + > + load_hci_header(hci_cmd, PS_VERIFY_CRC, 0, crc); > + > + if (send_hci_cmd_wait_event(dev, > + hci_cmd, > + sizeof(hci_cmd), > + &event, > + &buffer_to_free) == 0) { > + > + if (read_ps_event(event) == 0) > + retval = -1; > + > + if (buffer_to_free != NULL) > + free(buffer_to_free); > + } > + > + return retval; > +} > +static int get_device_type(int dev, uint32_t *code) > +{ > + uint8_t hci_cmd[] = { 0x05, 0xfc, 0x05, 0x00, 0x00, 0x00, 0x00, 0x04 }; > + uint8_t *event; > + uint8_t *buffer_to_free = NULL; > + uint32_t reg; > + int result = -1; > + > + *code = 0; > + > + hci_cmd[3] = (uint8_t) (DEV_REGISTER & 0xFF); > + hci_cmd[4] = (uint8_t) ((DEV_REGISTER >> 8) & 0xFF); > + hci_cmd[5] = (uint8_t) ((DEV_REGISTER >> 16) & 0xFF); > + hci_cmd[6] = (uint8_t) ((DEV_REGISTER >> 24) & 0xFF); > + > + if (send_hci_cmd_wait_event(dev, > + hci_cmd, > + sizeof(hci_cmd), > + &event, > + &buffer_to_free) == 0) { > + > + if (event[5] == 0xFC && event[6] == 0x00) { > + > + switch (event[4]) { > + case 0x05: > + reg = event[10]; > + reg = ((reg << 8) | event[9]); > + reg = ((reg << 8) | event[8]); > + reg = ((reg << 8) | event[7]); > + *code = reg; > + result = 0; > + > + break; > + > + case 0x06: > + break; > + } > + } > + } > + > + if (buffer_to_free) > + free(buffer_to_free); > + > + return result; > +} > + > +static int read_ar3k_version(int pConfig, int *rom_version, int *build_version) > +{ > + uint8_t hci_cmd[] = { 0x1E, 0xfc, 0x00 }; > + uint8_t *event; > + uint8_t *buffer_to_free = NULL; > + int result = -1; > + > + if (send_hci_cmd_wait_event(pConfig, > + hci_cmd, > + sizeof(hci_cmd), > + &event, > + &buffer_to_free) == 0) { > + > + if (event[5] == 0xFC && > + event[6] == 0x00 && > + event[4] == 0x1E) { > + > + (*rom_version) = event[10]; > + (*rom_version) = (((*rom_version) << 8) | event[9]); > + (*rom_version) = (((*rom_version) << 8) | event[8]); > + (*rom_version) = (((*rom_version) << 8) | event[7]); > + > + (*build_version) = event[14]; > + (*build_version) = (((*build_version) << 8) | > + event[13]); > + (*build_version) = (((*build_version) << 8) | > + event[12]); > + (*build_version) = (((*build_version) << 8) | > + event[11]); > + > + result = 1; > + > + } > + > + if (buffer_to_free) > + free(buffer_to_free); > + } > + > + return result; > +} > + > +static int str2bdaddr(char *str_bdaddr, char *bdaddr) > +{ > + char bdbyte[3]; > + char *str_byte = str_bdaddr; > + int i, j; > + unsigned char colon_present = 0; > + > + if (strstr(str_bdaddr, ":")) > + colon_present = 1; > + > + bdbyte[2] = '\0'; > + > + for (i = 0, j = 5; i < 6; i++, j--) { > + > + bdbyte[0] = str_byte[0]; > + bdbyte[1] = str_byte[1]; > + bdaddr[j] = strtol(bdbyte, NULL, 16); > + > + if (colon_present == 1) > + str_byte += 3; > + else > + str_byte += 2; > + } > + return 0; > +} > + > +static int write_bdaddr(int pConfig, char *bdaddr) > +{ > + uint8_t bdaddr_cmd[] = { 0x0B, 0xFC, 0x0A, > + 0x01, 0x01, 0x00, > + 0x06, 0x00, 0x00, > + 0x00, 0x00, 0x00, > + 0x00 }; > + uint8_t *event; > + uint8_t *buffer_to_free = NULL; > + int result = -1; > + > + str2bdaddr(bdaddr, (char *)&bdaddr_cmd[7]); > + > + if (send_hci_cmd_wait_event(pConfig, > + bdaddr_cmd, > + sizeof(bdaddr_cmd), > + &event, > + &buffer_to_free) == 0) { > + > + if (event[5] == 0xFC && event[6] == 0x00) { > + if (event[4] == 0x0B) > + result = 0; > + } > + > + } > + > + if (buffer_to_free) > + free(buffer_to_free); > + > + return result; > +} > + > +int ath_configure_sleep(int fd, int sleep_stat) > +{ > + int dev_id, dd; > + struct timespec tm = {0, 50000}; > + > + dev_id = ioctl(fd, HCIUARTGETDEVICE, 0); > + > + if (dev_id < 0) { > + perror("cannot get device id"); > + return -1; > + } > + > + dd = hci_open_dev(dev_id); > + > + if (dd < 0) { > + perror("HCI device open failed"); > + return -1; > + } > + > + sleep(2); > + > + /* send vendor specific command with Sleep feature Enabled */ > + if (hci_send_cmd(dd, OGF_VENDOR_CMD, 0x04, 1, &sleep_stat) < 0) > + perror("Power management Disabled"); > + > + nanosleep(&tm, NULL); > + hci_close_dev(dd); > + > + return 0; > +} > + > +int ath_ps_download(int hdev) > +{ > + int i; > + int status = 0; > + int tag_count; > + int patch_count; > + int total_tag_len = 0; > + int rom_version = 0, build_version = 0; > + > + struct ps_cmd_packet *hci_cmd_list; /* List storing the commands */ > + uint32_t num_cmds; > + uint8_t *event; > + uint8_t *buffer_to_free; > + uint32_t dev_type; > + > + char patch_file[PATH_MAX]; > + char ps_file[PATH_MAX]; > + char bdaddr_file[PATH_MAX]; > + > + FILE *stream; > + char bdaddr[21]; > + > + hci_cmd_list = NULL; > + > + /* > + * Verfiy firmware version. depending on it select the PS > + * config file to download. > + */ > + if (get_device_type(hdev, &dev_type) == -1) { > + status = -1; > + goto download_cmplete; > + } > + if (read_ar3k_version(hdev, &rom_version, &build_version) == -1) { > + status = -1; > + goto download_cmplete; > + } > + > + get_ps_file_name(dev_type, rom_version, ps_file); > + > + get_patch_file_name(dev_type, rom_version, build_version, patch_file); > + > + /* Read the PS file to a dynamically allocated buffer */ > + stream = fopen(ps_file, "r"); > + > + if (!stream) { > + perror("firmware file open error\n"); > + status = -1; > + goto download_cmplete; > + } > + > + tag_count = ath_parse_ps(stream, &total_tag_len); > + > + fclose(stream); > + > + if (tag_count == -1) { > + status = -1; > + goto download_cmplete; > + } > + > + /* > + * It is not necessary that Patch file be available, > + * continue with PS Operations if. > + * failed. > + */ > + if (patch_file[0] == '\0') > + status = 0; > + > + stream = fopen(patch_file, "r"); > + > + if (!stream) { > + patch_count = 0; > + status = 0; > + } else { > + /* parse and store the Patch file contents to > + * a global variables > + */ > + patch_count = parse_patch_file(stream); > + > + fclose(stream); > + > + if (patch_count < 0) { > + status = -1; > + goto download_cmplete; > + } > + } > + > + /* > + * Send the CRC packet, > + * Continue with the PS operations > + * only if the CRC check failed > + */ > + if (get_ar3k_crc(hdev, tag_count, patch_count) < 0) { > + status = 0; > + goto download_cmplete; > + } > + > + /* Create an HCI command list > + * from the parsed PS and patch information > + */ > + ath_create_cmd_list(&hci_cmd_list, > + &num_cmds, > + tag_count, > + patch_count, > + total_tag_len); > + > + for (i = 0; i < num_cmds; i++) { > + > + if (send_hci_cmd_wait_event > + (hdev, > + hci_cmd_list[i].Hcipacket, > + hci_cmd_list[i].packetLen, > + &event, > + &buffer_to_free) == 0) { > + > + if (read_ps_event(event) < 0) { > + > + /* Exit if the status is not success */ > + if (buffer_to_free) > + free(buffer_to_free); > + > + status = -1; > + goto download_cmplete; > + } > + if (buffer_to_free) > + free(buffer_to_free); > + } else { > + status = 0; > + goto download_cmplete; > + } > + } > + /* Read the PS file to a dynamically allocated buffer */ > + sprintf(bdaddr_file, "%s%x/%s", FW_PATH, rom_version, BDADDR_FILE); > + > + stream = fopen(bdaddr_file, "r"); > + > + if (!stream) { > + status = 0; > + goto download_cmplete; > + } > + > + if (fgets(bdaddr, 20, stream)) > + status = write_bdaddr(hdev, bdaddr); > + > + fclose(stream); > + > +download_cmplete: > + > + if (hci_cmd_list) > + ath_free_command_list(&hci_cmd_list, num_cmds); > + > + return status; > +} Please spend some time to verify the patch so that I can take it to the next level. Regards Suraj ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v3] hciattach application support for Atheros AR300x Bluetooth Chip 2010-05-12 13:47 ` [PATCH v3] hciattach application support for " suraj 2010-05-20 13:37 ` suraj @ 2010-05-20 16:00 ` Marcel Holtmann 2010-05-21 5:01 ` Suraj Sumangala 1 sibling, 1 reply; 36+ messages in thread From: Marcel Holtmann @ 2010-05-20 16:00 UTC (permalink / raw) To: suraj; +Cc: linux-bluetooth, Luis.Rodriguez, Jothikumar.Mothilal Hi Suraj, > Implements support for Atheros AR300x Bluetooth chip in hciattach. > Adds feature to bring up AR300x Bluetooth chip > with and without enhanced power management enabled. > > Signed-off-by: Suraj <suraj@Atheros.com> not SOBs for BlueZ userspace. That is kernel only. > --- > Makefile.tools | 1 + > tools/hciattach.8 | 6 + > tools/hciattach.c | 111 +++++ > tools/hciattach.h | 3 + > tools/hciattach_ar3k.c | 1223 ++++++++++++++++++++++++++++++++++++++++++++++++ > 5 files changed, 1344 insertions(+), 0 deletions(-) > create mode 100755 tools/hciattach_ar3k.c While hciattach is kinda the bad sheep inside BlueZ, I will enforce the general coding style for it for all new contributions. So could you please fix that one first. Otherwise it makes no sense for me to go through such a big patch if I have to complain about the coding style breakage for every second line. A general rule is if it fails check-patch.pl from the kernel, then you are doing something wrong. Regards Marcel ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v3] hciattach application support for Atheros AR300x Bluetooth Chip 2010-05-20 16:00 ` Marcel Holtmann @ 2010-05-21 5:01 ` Suraj Sumangala 2010-05-21 7:34 ` Marcel Holtmann 0 siblings, 1 reply; 36+ messages in thread From: Suraj Sumangala @ 2010-05-21 5:01 UTC (permalink / raw) To: Marcel Holtmann Cc: Suraj Sumangala, linux-bluetooth@vger.kernel.org, Luis Rodriguez, Jothikumar Mothilal Hi Marcel, Marcel Holtmann wrote: > Hi Suraj, > >> Implements support for Atheros AR300x Bluetooth chip in hciattach. >> Adds feature to bring up AR300x Bluetooth chip >> with and without enhanced power management enabled. >> >> Signed-off-by: Suraj <suraj@Atheros.com> > > not SOBs for BlueZ userspace. That is kernel only. > >> --- >> Makefile.tools | 1 + >> tools/hciattach.8 | 6 + >> tools/hciattach.c | 111 +++++ >> tools/hciattach.h | 3 + >> tools/hciattach_ar3k.c | 1223 ++++++++++++++++++++++++++++++++++++++++++++++++ >> 5 files changed, 1344 insertions(+), 0 deletions(-) >> create mode 100755 tools/hciattach_ar3k.c > > While hciattach is kinda the bad sheep inside BlueZ, I will enforce the > general coding style for it for all new contributions. So could you > please fix that one first. Otherwise it makes no sense for me to go > through such a big patch if I have to complain about the coding style > breakage for every second line. A general rule is if it fails > check-patch.pl from the kernel, then you are doing something wrong. > > Regards > > Marcel > > I had verified this patch using checkpatch and it did not complain. I had also verified it manually according to the comments others have given previously for different patches. If you still see issues, then we can add that check too to checkpatch if possible. Being a big patch, there are bound to be problems.But I would appreciate if you could review atleast part of it and give your comments. I can do the changes for the full patch. Also I would request others in the community to pitch in. Regards Suraj ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v3] hciattach application support for Atheros AR300x Bluetooth Chip 2010-05-21 5:01 ` Suraj Sumangala @ 2010-05-21 7:34 ` Marcel Holtmann 0 siblings, 0 replies; 36+ messages in thread From: Marcel Holtmann @ 2010-05-21 7:34 UTC (permalink / raw) To: Suraj Sumangala Cc: Suraj Sumangala, linux-bluetooth@vger.kernel.org, Luis Rodriguez, Jothikumar Mothilal Hi Suraj, > >> Implements support for Atheros AR300x Bluetooth chip in hciattach. > >> Adds feature to bring up AR300x Bluetooth chip > >> with and without enhanced power management enabled. > >> > >> Signed-off-by: Suraj <suraj@Atheros.com> > > > > not SOBs for BlueZ userspace. That is kernel only. > > > >> --- > >> Makefile.tools | 1 + > >> tools/hciattach.8 | 6 + > >> tools/hciattach.c | 111 +++++ > >> tools/hciattach.h | 3 + > >> tools/hciattach_ar3k.c | 1223 ++++++++++++++++++++++++++++++++++++++++++++++++ > >> 5 files changed, 1344 insertions(+), 0 deletions(-) > >> create mode 100755 tools/hciattach_ar3k.c > > > > While hciattach is kinda the bad sheep inside BlueZ, I will enforce the > > general coding style for it for all new contributions. So could you > > please fix that one first. Otherwise it makes no sense for me to go > > through such a big patch if I have to complain about the coding style > > breakage for every second line. A general rule is if it fails > > check-patch.pl from the kernel, then you are doing something wrong. > > I had verified this patch using checkpatch and it did not complain. I > had also verified it manually according to the comments others have > given previously for different patches. > > If you still see issues, then we can add that check too to checkpatch if > possible. then maybe checkpatch.pl is not good enough for this. I see way too many empty lines, weird if breakages, and many more. So as I said, this needs cleanup first. Regards Marcel ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip 2010-04-22 9:10 ` [PATCH v5] " suraj 2010-04-26 11:00 ` suraj 2010-05-12 13:47 ` [PATCH v3] hciattach application support for " suraj @ 2010-05-20 16:09 ` Marcel Holtmann 2 siblings, 0 replies; 36+ messages in thread From: Marcel Holtmann @ 2010-05-20 16:09 UTC (permalink / raw) To: suraj; +Cc: linux-bluetooth, Luis.Rodriguez, Jothikumar.Mothilal, gfpadovan Hi Suraj, for every new patch version, please start a new thread. If you re-use an old thread, I will most likely ignore it. > +static struct sk_buff *ath_dequeue(struct hci_uart *hu) > +{ > + struct ath_struct *ath = hu->priv; > + struct sk_buff *skbuf; > + > + skbuf = skb_dequeue(&ath->txq); > + > + if (!skbuf) > + return NULL; > + > + /* > + * Check if the HCI command is HCI sleep enable and > + * update the sleep enable flag with command parameter. > + * > + * Value of sleep enable flag will be used later > + * to verify if controller has to be woken up before > + * sending any packet. > + */ > + if (skbuf->data[0] == 0x01 && > + skbuf->data[1] == 0x04 && > + skbuf->data[2] == 0xFC) > + ath->cur_sleep = skbuf->data[4]; > + > + return skbuf; > +} I don't really like this. I know we don't have any infrastructure to forward vendor commands back to the driver. And we might need to change that actually. However at least for now use the headers and constants and not magic numbers here. > +static void ath_check_data_len(struct ath_struct *ath, int len) > +{ > + int room = skb_tailroom(ath->rx_skb); > + > + BT_DBG("len %d room %d", len, room); > + > + if (len > room) { > + BT_ERR("Data length is too large"); > + kfree_skb(ath->rx_skb); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_skb = NULL; > + ath->rx_count = 0; > + } else { > + ath->rx_state = HCIATH_W4_DATA; > + ath->rx_count = len; > + } > +} > + > +/* Recv data */ > +static int ath_recv(struct hci_uart *hu, void *data, int count) > +{ > + struct ath_struct *ath = hu->priv; > + char *ptr = data; > + struct hci_event_hdr *eh; > + struct hci_acl_hdr *ah; > + struct hci_sco_hdr *sh; > + int len, type, dlen; > + > + BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, > + ath->rx_state, ath->rx_count); > + > + while (count) { > + if (ath->rx_count) { > + > + len = min_t(unsigned int, ath->rx_count, count); > + memcpy(skb_put(ath->rx_skb, len), ptr, len); > + ath->rx_count -= len; > + count -= len; > + ptr += len; > + > + if (ath->rx_count) > + continue; > + switch (ath->rx_state) { > + case HCIATH_W4_DATA: > + hci_recv_frame(ath->rx_skb); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_skb = NULL; > + ath->rx_count = 0; > + continue; > + > + case HCIATH_W4_EVENT_HDR: > + eh = hci_event_hdr(ath->rx_skb); > + > + BT_DBG("Event header: evt 0x%2.2x plen %d", > + eh->evt, eh->plen); > + > + ath_check_data_len(ath, eh->plen); > + continue; > + > + case HCIATH_W4_ACL_HDR: > + ah = hci_acl_hdr(ath->rx_skb); > + dlen = __le16_to_cpu(ah->dlen); > + > + BT_DBG("ACL header: dlen %d", dlen); > + > + ath_check_data_len(ath, dlen); > + continue; > + > + case HCIATH_W4_SCO_HDR: > + sh = hci_sco_hdr(ath->rx_skb); > + > + BT_DBG("SCO header: dlen %d", sh->dlen); > + > + ath_check_data_len(ath, sh->dlen); > + continue; > + > + } > + } > + > + /* HCIATH_W4_PACKET_TYPE */ > + switch (*ptr) { > + case HCI_EVENT_PKT: > + BT_DBG("Event packet"); > + ath->rx_state = HCIATH_W4_EVENT_HDR; > + ath->rx_count = HCI_EVENT_HDR_SIZE; > + type = HCI_EVENT_PKT; > + break; > + > + case HCI_ACLDATA_PKT: > + BT_DBG("ACL packet"); > + ath->rx_state = HCIATH_W4_ACL_HDR; > + ath->rx_count = HCI_ACL_HDR_SIZE; > + type = HCI_ACLDATA_PKT; > + break; > + > + case HCI_SCODATA_PKT: > + BT_DBG("SCO packet"); > + ath->rx_state = HCIATH_W4_SCO_HDR; > + ath->rx_count = HCI_SCO_HDR_SIZE; > + type = HCI_SCODATA_PKT; > + break; > + > + default: > + BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); > + hu->hdev->stat.err_rx++; > + ptr++; > + count--; > + continue; > + > + }; > + ptr++; > + count--; > + > + /* Allocate packet */ > + ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); > + if (!ath->rx_skb) { > + BT_ERR("Can't allocate mem for new packet"); > + ath->rx_state = HCIATH_W4_PACKET_TYPE; > + ath->rx_count = 0; > + > + return -ENOMEM; > + } > + ath->rx_skb->dev = (void *)hu->hdev; > + bt_cb(ath->rx_skb)->pkt_type = type; > + } > + > + return count; > +} What was the reason that hci_recv_fragment is not good enough here and can not made work for this case. I really wanna avoid have multiple implementation of this reassembly support all over the places. Regards Marcel ^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH] Added support for Atheros AR300x UART Bluetooth Chip @ 2010-02-09 11:43 Vikram Kandukuri 2010-03-11 13:18 ` [PATCH] Added support for Atheros AR300x " Suraj Sumangala 0 siblings, 1 reply; 36+ messages in thread From: Vikram Kandukuri @ 2010-02-09 11:43 UTC (permalink / raw) To: linux-bluetooth; +Cc: marcel, suraj, lrodriguez Signed-off-by: Suraj <suraj@atheros.com> --- drivers/bluetooth/Kconfig | 10 + drivers/bluetooth/Makefile | 1 + drivers/bluetooth/hci_ath.c | 545 +++++++++++++++++++++++++++++++++++++++++ drivers/bluetooth/hci_ath.h | 72 ++++++ drivers/bluetooth/hci_ldisc.c | 6 + drivers/bluetooth/hci_uart.h | 8 +- 6 files changed, 641 insertions(+), 1 deletions(-) create mode 100755 drivers/bluetooth/hci_ath.c create mode 100755 drivers/bluetooth/hci_ath.h diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 058fbcc..32b98a4 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -58,6 +58,16 @@ config BT_HCIUART_BCSP Say Y here to compile support for HCI BCSP protocol. +config BT_HCIUART_ATH + bool "Atheros AR3001 Board support" + depends on BT_HCIUART + help + HCIATH (HCI Atheros) is a serial protocol for communication + between Bluetooth device and host. This protocol is required for + serial Bluetooth devices that are based on Atheros AR3001 chips. + + Say Y here to compile support for HCIATH protocol. + config BT_HCIUART_LL bool "HCILL protocol support" depends on BT_HCIUART diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 7e5aed5..1481faa 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -26,4 +26,5 @@ hci_uart-y := hci_ldisc.o hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o +hci_uart-$(CONFIG_BT_HCIUART_ATH) += hci_ath.o hci_uart-objs := $(hci_uart-y) diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c new file mode 100755 index 0000000..abacc09 --- /dev/null +++ b/drivers/bluetooth/hci_ath.c @@ -0,0 +1,545 @@ +/* + * Copyright (c) 2009-2010 Atheros Communications Inc. + * + * 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 <linux/module.h> +#include <linux/kernel.h> + +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/poll.h> + +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/signal.h> +#include <linux/ioctl.h> +#include <linux/skbuff.h> +#include <linux/firmware.h> +#include <linux/wait.h> +#include <linux/timer.h> +#include <linux/tty.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci.h> + +#include "hci_uart.h" +#include "hci_ath.h" +static void ath_context_switch(struct work_struct *work) +{ + int status; + struct ath_struct *ath; + struct hci_uart *hu; + struct tty_struct *tty; + struct sk_buff *skbuf; + ath = container_of(work, struct ath_struct, ctxtsw); + hu = ath->hu; + tty = hu->tty; + status = ath_wakeup_ar3001(tty); + if ((status & TIOCM_CTS)) { + while ((skbuf = skb_dequeue(&ath->tx_wait_q))) + skb_queue_tail(&ath->txq, skbuf); + + /* Ready to send Data */ + clear_bit(HCI_UART_SENDING, &hu->tx_state); + hci_uart_tx_wakeup(hu); + } +} + +/* Initialize protocol */ +static int ath_open(struct hci_uart *hu) +{ + struct ath_struct *ath; + BT_DBG("hu %p", hu); + ath = kzalloc(sizeof(*ath), GFP_ATOMIC); + if (!ath) + return -ENOMEM; + skb_queue_head_init(&ath->txq); + skb_queue_head_init(&ath->tx_wait_q); + skb_queue_head_init(&ath->tx_cmd_wait_q); + spin_lock_init(&ath->hciath_lock); + ath->cur_sleep = 0; + ath->num_cmds_complete = 1; + hu->priv = ath; + ath->hu = hu; + init_waitqueue_head(&ath->wqevt); + INIT_WORK(&ath->ctxtsw, ath_context_switch); + return 0; +} + +/* Flush protocol data */ +static int ath_flush(struct hci_uart *hu) +{ + struct ath_struct *ath = hu->priv; + BT_DBG("hu %p", hu); + skb_queue_purge(&ath->tx_wait_q); + skb_queue_purge(&ath->txq); + skb_queue_purge(&ath->tx_cmd_wait_q); + return 0; +} + +/* Close protocol */ +static int ath_close(struct hci_uart *hu) +{ + struct ath_struct *ath; + ath = hu->priv; + BT_DBG("hu %p", hu); + skb_queue_purge(&ath->tx_wait_q); + skb_queue_purge(&ath->txq); + skb_queue_purge(&ath->tx_cmd_wait_q); + if (ath->rx_skb) + kfree_skb(ath->rx_skb); + wake_up_interruptible(&ath->wqevt); + hu->priv = NULL; + kfree(ath); + return 0; +} + +/* Enqueue frame for transmittion (padding, crc, etc) */ +/* may be called from two simultaneous tasklets */ +static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) +{ + struct ath_struct *ath; + ath = hu->priv; + if (HCI_SCODATA_PKT == bt_cb(skb)->pkt_type) { + + /* Discard SCO packet as AR3001 does not support SCO over HCI */ + BT_DBG("SCO Packet over HCI received Dropping\n"); + kfree(skb); + return 0; + } + BT_DBG("hu %p skb %p", hu, skb); + + /* Prepend skb with frame type */ + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + if (ath->num_cmds_complete <= 0 + && HCI_COMMAND_PKT == bt_cb(skb)->pkt_type) { + skb_queue_tail(&ath->tx_cmd_wait_q, skb); + return 0; + } + + skb_queue_tail(&ath->txq, skb); + set_bit(HCI_UART_SENDING, &hu->tx_state); + + schedule_work(&ath->ctxtsw); + return 0; +} + +static struct sk_buff *ath_dequeue(struct hci_uart *hu) +{ + struct ath_struct *ath; + struct sk_buff *skbuff; + ath = hu->priv; + skbuff = skb_dequeue(&ath->txq); + if (NULL != skbuff) { + ath_handle_host_data(ath, bt_cb(skbuff)->pkt_type, + &skbuff->data[1], skbuff->len - 1); + } + return skbuff; +} + +static inline int ath_check_data_len(struct ath_struct *ath, int len) +{ + register int room = skb_tailroom(ath->rx_skb); + BT_DBG("len %d room %d", len, room); + if (!len) { + hci_recv_frame(ath->rx_skb); + } else if (len > room) { + BT_ERR("Data length is too large"); + kfree_skb(ath->rx_skb); + } else { + ath->rx_state = HCIATH_W4_DATA; + ath->rx_count = len; + return len; + } + ath->rx_state = HCIATH_W4_PACKET_TYPE; + ath->rx_skb = NULL; + ath->rx_count = 0; + return 0; +} + +/* Recv data */ +static int ath_recv(struct hci_uart *hu, void *data, int count) +{ + struct ath_struct *ath = hu->priv; + register char *ptr; + struct hci_event_hdr *eh; + struct hci_acl_hdr *ah; + struct hci_sco_hdr *sh; + struct sk_buff *skbuff; + register int len, type, dlen; + skbuff = NULL; + BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, + ath->rx_state, ath->rx_count); + ptr = data; + while (count) { + if (ath->rx_count) { + len = min_t(unsigned int, ath->rx_count, count); + memcpy(skb_put(ath->rx_skb, len), ptr, len); + ath->rx_count -= len; + count -= len; + ptr += len; + if (ath->rx_count) + continue; + switch (ath->rx_state) { + case HCIATH_W4_DATA: + ath_handle_data_from_controller(ath, + bt_cb + (ath->rx_skb)-> + pkt_type, + ath->rx_skb-> + data, + ath->rx_skb-> + len); + if (HCI_EVENT_PKT == + bt_cb(ath->rx_skb)->pkt_type + && ath->num_cmds_complete > 0) { + + skbuff = + skb_dequeue(&ath->tx_cmd_wait_q); + if (skbuff != NULL) { + skb_queue_tail(&ath->txq, + skbuff); + schedule_work(&ath->ctxtsw); + } + } + if (ath_verify_event_discardable + (hu, bt_cb(ath->rx_skb)->pkt_type, + ath->rx_skb->data)) { + kfree(ath->rx_skb); + ath->rx_skb = NULL; + } else { + hci_recv_frame(ath->rx_skb); + } + ath->rx_state = HCIATH_W4_PACKET_TYPE; + ath->rx_skb = NULL; + ath->rx_count = 0; + continue; + case HCIATH_W4_EVENT_HDR: + eh = (struct hci_event_hdr *)ath->rx_skb->data; + BT_DBG("Event header: evt 0x%2.2x plen %d", + eh->evt, eh->plen); + ath_check_data_len(ath, eh->plen); + continue; + case HCIATH_W4_ACL_HDR: + ah = (struct hci_acl_hdr *)ath->rx_skb->data; + dlen = __le16_to_cpu(ah->dlen); + BT_DBG("ACL header: dlen %d", dlen); + ath_check_data_len(ath, dlen); + continue; + case HCIATH_W4_SCO_HDR: + sh = (struct hci_sco_hdr *)ath->rx_skb->data; + BT_DBG("SCO header: dlen %d", sh->dlen); + ath_check_data_len(ath, sh->dlen); + continue; + } + } + + /* HCIATH_W4_PACKET_TYPE */ + switch (*ptr) { + case HCI_EVENT_PKT: + BT_DBG("Event packet"); + ath->rx_state = HCIATH_W4_EVENT_HDR; + ath->rx_count = HCI_EVENT_HDR_SIZE; + type = HCI_EVENT_PKT; + break; + case HCI_ACLDATA_PKT: + BT_DBG("ACL packet"); + ath->rx_state = HCIATH_W4_ACL_HDR; + ath->rx_count = HCI_ACL_HDR_SIZE; + type = HCI_ACLDATA_PKT; + break; + case HCI_SCODATA_PKT: + BT_DBG("SCO packet"); + ath->rx_state = HCIATH_W4_SCO_HDR; + ath->rx_count = HCI_SCO_HDR_SIZE; + type = HCI_SCODATA_PKT; + break; + default: + BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); + hu->hdev->stat.err_rx++; + ptr++; + count--; + continue; + }; + ptr++; + count--; + + /* Allocate packet */ + ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + if (!ath->rx_skb) { + BT_ERR("Can't allocate mem for new packet"); + ath->rx_state = HCIATH_W4_PACKET_TYPE; + ath->rx_count = 0; + return 0; + } + ath->rx_skb->dev = (void *)hu->hdev; + bt_cb(ath->rx_skb)->pkt_type = type; + } return count; +} + +static void ath_controller_sleep_mode(struct hci_uart *hu, bool enable) +{ + unsigned char sleepcmd[] = { 0x01, 0x04, 0xFC, 0x01, 0x00 }; + sleepcmd[4] = enable; + ath_write_data_to_cntrlr(hu->hdev, sleepcmd, sizeof(sleepcmd)); +} + +int ath_wakeup_ar3001(struct tty_struct *tty) +{ + struct termios settings; + int status = 0x00; + mm_segment_t oldfs; + status = tty->driver->ops->tiocmget(tty, NULL); + + if ((status & TIOCM_CTS)) + return status; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); + + settings.c_cflag &= ~CRTSCTS; + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); + set_fs(oldfs); + status = tty->driver->ops->tiocmget(tty, NULL); + + tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS); + mdelay(20); + + status = tty->driver->ops->tiocmget(tty, NULL); + + tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00); + mdelay(20); + + status = tty->driver->ops->tiocmget(tty, NULL); + oldfs = get_fs(); + set_fs(KERNEL_DS); + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); + + settings.c_cflag |= CRTSCTS; + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); + set_fs(oldfs); + return status; +} + +int ath_fullboot_config(struct hci_uart *hu, int current_event) +{ + struct sk_buff *skbuf; + struct ath_struct *ath = hu->priv; + static int autosleep; + unsigned char rstevt[] = { 0x1, 0x3, 0xc, 0x0 }; + if (current_event == HCI_RESET) { + + if (ath->cur_sleep) { + autosleep = 1; + ath_controller_sleep_mode(hu, 1); + return 1; + } else { + return 0; + } + } + + if (current_event == HCI_SET_SLEEP_MODE) { + + if (autosleep == 0) + return 1; + + while ((skbuf = skb_dequeue(&ath->tx_wait_q))) + skb_queue_tail(&ath->txq, skbuf); + + ath_write_data_to_host(hu->hdev, rstevt, sizeof(rstevt)); + autosleep = 0; + return 1; + } + return 0; +} + +int ath_write_data_to_host(void *dev, unsigned char *data, u8 length) +{ + struct sk_buff *skbuf; + struct hci_dev *hdev; + hdev = (struct hci_dev *)dev; + skbuf = bt_skb_alloc(length, GFP_ATOMIC); + if (!skbuf) { + BT_ERR("Memory allocation failed"); + return -1; + } + skb_orphan(skbuf); + + /* First byte will be added as packet type */ + bt_cb(skbuf)->pkt_type = data[0]; + skbuf->dev = (void *)hdev; + memcpy(skb_put(skbuf, length - 1), &data[1], length - 1); + hci_recv_frame(skbuf); + return length; +} + +int ath_write_data_to_cntrlr(void *dev, unsigned char *Data, u8 len) +{ + struct sk_buff *skbuff; + struct ath_struct *ath; + struct hci_uart *hu; + struct hci_dev *hdev; + if (NULL == dev) { + BT_DBG("NULL handle received %p \n", dev); + return 0; + } + hdev = (struct hci_dev *)dev; + hu = (struct hci_uart *)hdev->driver_data; + if (hu == NULL) { + BT_DBG("NULL handle received %p \n", hdev); + return 0; + } + ath = hu->priv; + if (ath == NULL) { + BT_DBG("NULL handle received \n"); + return 0; + } + skbuff = bt_skb_alloc(len, GFP_ATOMIC); + if (skbuff == NULL) { + BT_DBG("Malloc Fail memory %p \n", skbuff); + return 0; + } + skb_orphan(skbuff); + + if (len != 0) + memcpy(skb_put(skbuff, len), Data, len); + + bt_cb(skbuff)->pkt_type = HCI_COMMAND_PKT; + skbuff->dev = dev; + if (ath->num_cmds_complete > 0) { + skb_queue_tail(&ath->txq, skbuff); + schedule_work(&ath->ctxtsw); + } else { + skb_queue_tail(&ath->tx_cmd_wait_q, skbuff); + } + return len; +} + +int ath_check_sleep_cmd(struct ath_struct *ath, unsigned char *packet) +{ + if (packet[0] == HCI_EVENT_PKT && packet[1] == 0xFC) + ath->cur_sleep = packet[3]; + + return 0; +} + +int ath_verify_event_discardable(struct hci_uart *hu, unsigned char pkt_type, + unsigned char *packet) +{ + if (pkt_type != HCI_EVENT_PKT) + return 0; + + switch (packet[0]) { + case 0x0E: /* Command Complete Event */ + if (packet[3] == 0x03 && packet[4] == 0x0C) { + + /* Command complete for HCI Reset Received */ + return ath_fullboot_config(hu, HCI_RESET); + } else if (packet[3] == 0x04 && packet[4] == 0xFC) { + return ath_fullboot_config(hu, HCI_SET_SLEEP_MODE); + } + break; + } + return 0; +} + +/* Update the number of commands that can be sent to controller */ +void ath_handle_hci_cmd(struct ath_struct *ath, u8 * packet) +{ + spin_lock(&ath->hciath_lock); + ath->num_cmds_complete--; + spin_unlock(&ath->hciath_lock); +} + +void ath_handle_hci_event(struct ath_struct *ath, u8 * packet) +{ + switch (packet[0]) { + case 0x05: /* ACL Disconnection Complete Event */ + break; + case 0x03: /* ACL Connection Complete Event */ + break; + case 0x0E: /* Command Complete Event */ + spin_lock(&ath->hciath_lock); + ath->num_cmds_complete = packet[2]; + spin_unlock(&ath->hciath_lock); + break; + case 0x0F: /* Command Status Event */ + spin_lock(&ath->hciath_lock); + ath->num_cmds_complete = packet[3]; + spin_unlock(&ath->hciath_lock); + break; + } +} + +void ath_handle_host_data(struct ath_struct *ath, u8 pktType, u8 * packet, + unsigned int len) +{ + switch (pktType) { + case HCI_ACLDATA_PKT: /* ACL packets */ + break; + case HCI_COMMAND_PKT: /* HCI Commands */ + ath_handle_hci_cmd(ath, packet); + ath_check_sleep_cmd(ath, packet); + break; + } +} + +void ath_handle_data_from_controller(struct ath_struct *ath, u8 pktType, + u8 *packet, unsigned int len) +{ + switch (pktType) { + case HCI_ACLDATA_PKT: /* ACL packets */ + break; + case HCI_EVENT_PKT: /* HCI Events */ + ath_handle_hci_event(ath, packet); + break; + } +} + +static struct hci_uart_proto athp = { + .id = HCI_UART_ATH, + .open = ath_open, + .close = ath_close, + .recv = ath_recv, + .enqueue = ath_enqueue, + .dequeue = ath_dequeue, + .flush = ath_flush, +}; + +int ath_init(void) +{ + int err = hci_uart_register_proto(&athp); + if (!err) + BT_INFO("HCIATH protocol initialized"); + + else + BT_ERR("HCIATH protocol registration failed"); + return err; +} + +int ath_deinit(void) +{ + return hci_uart_unregister_proto(&athp); +} diff --git a/drivers/bluetooth/hci_ath.h b/drivers/bluetooth/hci_ath.h new file mode 100755 index 0000000..7513a89 --- /dev/null +++ b/drivers/bluetooth/hci_ath.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2009-2010 Atheros Communications Inc. + * + * 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 + * + */ + +#ifndef __HCI_ATH_H_ +#define __HCI_ATH_H_ + +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/signal.h> +#include <linux/timer.h> + +#include <linux/ioctl.h> +#include <linux/skbuff.h> +#include <linux/firmware.h> +#include <linux/wait.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +/* HCIATH receiver States */ +#define HCIATH_W4_PACKET_TYPE 0 +#define HCIATH_W4_EVENT_HDR 1 +#define HCIATH_W4_ACL_HDR 2 +#define HCIATH_W4_SCO_HDR 3 +#define HCIATH_W4_DATA 4 + +#define HCI_SET_SLEEP_MODE 0xFC04 +#define HCI_RESET 0x030C + struct ath_struct { + struct hci_uart *hu; + unsigned int rx_state; + unsigned int rx_count; + unsigned int cur_sleep; + int num_cmds_complete; + spinlock_t hciath_lock; + struct sk_buff *rx_skb; + struct sk_buff_head txq; + struct sk_buff_head tx_wait_q; + struct sk_buff_head tx_cmd_wait_q; + wait_queue_head_t wqevt; + struct work_struct ctxtsw; +}; +int ath_write_data_to_cntrlr(void *dev, unsigned char *Data, u8 len); +int ath_write_data_to_host(void *dev, unsigned char *data, u8 length); +int ath_wakeup_ar3001(struct tty_struct *tty); +int ath_verify_event_discardable(struct hci_uart *hu, unsigned char pkt_type, + unsigned char *packet); +int ath_check_sleep_cmd(struct ath_struct *ath, unsigned char *packet); +void ath_handle_host_data(struct ath_struct *ath, u8 pktType, u8 *packet, + unsigned int len); +void ath_handle_data_from_controller(struct ath_struct *ath, u8 pktType, + u8 *packet, unsigned int len); + + +#endif /* __HCI_ATH_H_ */ diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index aa09193..bd16130 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -542,6 +542,9 @@ static int __init hci_uart_init(void) #ifdef CONFIG_BT_HCIUART_LL ll_init(); #endif +#ifdef CONFIG_BT_HCIUART_ATH + ath_init(); +#endif return 0; } @@ -559,6 +562,9 @@ static void __exit hci_uart_exit(void) #ifdef CONFIG_BT_HCIUART_LL ll_deinit(); #endif +#ifdef CONFIG_BT_HCIUART_ATH + ath_deinit(); +#endif /* Release tty registration of line discipline */ if ((err = tty_unregister_ldisc(N_HCI))) diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 50113db..385537f 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -33,13 +33,14 @@ #define HCIUARTGETDEVICE _IOR('U', 202, int) /* UART protocols */ -#define HCI_UART_MAX_PROTO 5 +#define HCI_UART_MAX_PROTO 6 #define HCI_UART_H4 0 #define HCI_UART_BCSP 1 #define HCI_UART_3WIRE 2 #define HCI_UART_H4DS 3 #define HCI_UART_LL 4 +#define HCI_UART_ATH 5 struct hci_uart; @@ -91,3 +92,8 @@ int bcsp_deinit(void); int ll_init(void); int ll_deinit(void); #endif + +#ifdef CONFIG_BT_HCIUART_ATH +int ath_init(void); +int ath_deinit(void); +#endif -- 1.6.3.3 ^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH] Added support for Atheros AR300x Bluetooth Chip 2010-02-09 11:43 [PATCH] Added support for Atheros AR300x UART " Vikram Kandukuri @ 2010-03-11 13:18 ` Suraj Sumangala 2010-03-11 17:15 ` Marcel Holtmann 0 siblings, 1 reply; 36+ messages in thread From: Suraj Sumangala @ 2010-03-11 13:18 UTC (permalink / raw) To: linux-bluetooth@vger.kernel.org Cc: Vikram Kandukuri, marcel@holtmann.org, Suraj Sumangala, Luis Rodriguez, Jothikumar Mothilal Signed-off-by: Suraj <suraj@atheros.com> --- drivers/bluetooth/Kconfig | 11 ++ drivers/bluetooth/Makefile | 1 + drivers/bluetooth/hci_ath.c | 353 +++++++++++++++++++++++++++++++++++++++++ drivers/bluetooth/hci_ldisc.c | 6 + drivers/bluetooth/hci_uart.h | 8 +- 5 files changed, 378 insertions(+), 1 deletions(-) create mode 100755 drivers/bluetooth/hci_ath.c diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 058fbcc..81abeff 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -58,6 +58,17 @@ config BT_HCIUART_BCSP Say Y here to compile support for HCI BCSP protocol. +config BT_HCIUART_ATH + bool "Atheros AR300x Board support" + depends on BT_HCIUART + help + HCIATH (HCI Atheros) is a serial protocol for communication + between Bluetooth device and host with support for Atheros AR300x + power management feature. This protocol is required for + serial Bluetooth devices that are based on Atheros AR300x chips. + + Say Y here to compile support for Atheros AR300x Chips. + config BT_HCIUART_LL bool "HCILL protocol support" depends on BT_HCIUART diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 7e5aed5..1481faa 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -26,4 +26,5 @@ hci_uart-y := hci_ldisc.o hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o +hci_uart-$(CONFIG_BT_HCIUART_ATH) += hci_ath.o hci_uart-objs := $(hci_uart-y) diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c new file mode 100755 index 0000000..13e4404 --- /dev/null +++ b/drivers/bluetooth/hci_ath.c @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2009-2010 Atheros Communications Inc. + * + * 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 <linux/module.h> +#include <linux/kernel.h> + +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/errno.h> +#include <linux/ioctl.h> +#include <linux/skbuff.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +#include "hci_uart.h" + + +/* HCIATH receiver States */ +#define HCIATH_W4_PACKET_TYPE 0 +#define HCIATH_W4_EVENT_HDR 1 +#define HCIATH_W4_ACL_HDR 2 +#define HCIATH_W4_SCO_HDR 3 +#define HCIATH_W4_DATA 4 + +struct ath_struct { + struct hci_uart *hu; + unsigned int rx_state; + unsigned int rx_count; + unsigned int cur_sleep; + + spinlock_t hciath_lock; + struct sk_buff *rx_skb; + struct sk_buff_head txq; + wait_queue_head_t wqevt; + struct work_struct ctxtsw; +}; + +int ath_wakeup_ar3001(struct tty_struct *tty) +{ + struct termios settings; + int status = 0x00; + mm_segment_t oldfs; + status = tty->driver->ops->tiocmget(tty, NULL); + + if ((status & TIOCM_CTS)) + return status; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); + + settings.c_cflag &= ~CRTSCTS; + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); + set_fs(oldfs); + status = tty->driver->ops->tiocmget(tty, NULL); + + /* Wake up board */ + tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS); + mdelay(20); + + status = tty->driver->ops->tiocmget(tty, NULL); + + tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00); + mdelay(20); + + status = tty->driver->ops->tiocmget(tty, NULL); + oldfs = get_fs(); + set_fs(KERNEL_DS); + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); + + settings.c_cflag |= CRTSCTS; + n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); + set_fs(oldfs); + return status; +} + +static void ath_context_switch(struct work_struct *work) +{ + int status; + struct ath_struct *ath; + struct hci_uart *hu; + struct tty_struct *tty; + + ath = container_of(work, struct ath_struct, ctxtsw); + + hu = ath->hu; + tty = hu->tty; + + /* verify and wake up controller */ + if (ath->cur_sleep) { + + status = ath_wakeup_ar3001(tty); + if (!(status & TIOCM_CTS)) + return; + } + + /* Ready to send Data */ + clear_bit(HCI_UART_SENDING, &hu->tx_state); + hci_uart_tx_wakeup(hu); +} + +int ath_check_sleep_cmd(struct ath_struct *ath, unsigned char *packet) +{ + if (packet[0] == 0x04 && packet[1] == 0xFC) + ath->cur_sleep = packet[3]; + + return 0; +} + + +/* Initialize protocol */ +static int ath_open(struct hci_uart *hu) +{ + struct ath_struct *ath; + BT_DBG("hu %p", hu); + + ath = kzalloc(sizeof(*ath), GFP_ATOMIC); + if (!ath) + return -ENOMEM; + + skb_queue_head_init(&ath->txq); + spin_lock_init(&ath->hciath_lock); + + ath->cur_sleep = 0; + hu->priv = ath; + ath->hu = hu; + + init_waitqueue_head(&ath->wqevt); + INIT_WORK(&ath->ctxtsw, ath_context_switch); + return 0; +} + +/* Flush protocol data */ +static int ath_flush(struct hci_uart *hu) +{ + struct ath_struct *ath = hu->priv; + BT_DBG("hu %p", hu); + skb_queue_purge(&ath->txq); + + return 0; +} + +/* Close protocol */ +static int ath_close(struct hci_uart *hu) +{ + struct ath_struct *ath = hu->priv; + BT_DBG("hu %p", hu); + + skb_queue_purge(&ath->txq); + + if (ath->rx_skb) + kfree_skb(ath->rx_skb); + + wake_up_interruptible(&ath->wqevt); + hu->priv = NULL; + kfree(ath); + return 0; +} + +/* Enqueue frame for transmittion */ +static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) +{ + struct ath_struct *ath = hu->priv; + if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { + + /* Discard SCO packet.AR3001 does not support SCO over HCI */ + BT_DBG("SCO Packet over HCI received Dropping\n"); + kfree(skb); + return 0; + } + BT_DBG("hu %p skb %p", hu, skb); + + /* Prepend skb with frame type */ + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + + skb_queue_tail(&ath->txq, skb); + set_bit(HCI_UART_SENDING, &hu->tx_state); + + schedule_work(&ath->ctxtsw); + return 0; +} + +static struct sk_buff *ath_dequeue(struct hci_uart *hu) +{ + struct ath_struct *ath = hu->priv; + struct sk_buff *skbuf; + + skbuf = skb_dequeue(&ath->txq); + if (skbuf != NULL) + ath_check_sleep_cmd(ath, &skbuf->data[1]); + + return skbuf; +} + +static inline int ath_check_data_len(struct ath_struct *ath, int len) +{ + register int room = skb_tailroom(ath->rx_skb); + BT_DBG("len %d room %d", len, room); + + if (len > room) { + BT_ERR("Data length is too large"); + kfree_skb(ath->rx_skb); + ath->rx_state = HCIATH_W4_PACKET_TYPE; + ath->rx_skb = NULL; + ath->rx_count = 0; + } else { + ath->rx_state = HCIATH_W4_DATA; + ath->rx_count = len; + return len; + } + + return 0; +} + +/* Recv data */ +static int ath_recv(struct hci_uart *hu, void *data, int count) +{ + struct ath_struct *ath = hu->priv; + register char *ptr; + struct hci_event_hdr *eh; + struct hci_acl_hdr *ah; + struct hci_sco_hdr *sh; + struct sk_buff *skbuf; + register int len, type, dlen; + + skbuf = NULL; + BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, + ath->rx_state, ath->rx_count); + ptr = data; + while (count) { + if (ath->rx_count) { + + len = min_t(unsigned int, ath->rx_count, count); + memcpy(skb_put(ath->rx_skb, len), ptr, len); + ath->rx_count -= len; + count -= len; + ptr += len; + + if (ath->rx_count) + continue; + switch (ath->rx_state) { + case HCIATH_W4_DATA: + hci_recv_frame(ath->rx_skb); + ath->rx_state = HCIATH_W4_PACKET_TYPE; + ath->rx_skb = NULL; + ath->rx_count = 0; + continue; + case HCIATH_W4_EVENT_HDR: + eh = (struct hci_event_hdr *)ath->rx_skb->data; + BT_DBG("Event header: evt 0x%2.2x plen %d", + eh->evt, eh->plen); + ath_check_data_len(ath, eh->plen); + continue; + case HCIATH_W4_ACL_HDR: + ah = (struct hci_acl_hdr *)ath->rx_skb->data; + dlen = __le16_to_cpu(ah->dlen); + BT_DBG("ACL header: dlen %d", dlen); + ath_check_data_len(ath, dlen); + continue; + case HCIATH_W4_SCO_HDR: + sh = (struct hci_sco_hdr *)ath->rx_skb->data; + BT_DBG("SCO header: dlen %d", sh->dlen); + ath_check_data_len(ath, sh->dlen); + continue; + } + } + + /* HCIATH_W4_PACKET_TYPE */ + switch (*ptr) { + case HCI_EVENT_PKT: + BT_DBG("Event packet"); + ath->rx_state = HCIATH_W4_EVENT_HDR; + ath->rx_count = HCI_EVENT_HDR_SIZE; + type = HCI_EVENT_PKT; + break; + case HCI_ACLDATA_PKT: + BT_DBG("ACL packet"); + ath->rx_state = HCIATH_W4_ACL_HDR; + ath->rx_count = HCI_ACL_HDR_SIZE; + type = HCI_ACLDATA_PKT; + break; + case HCI_SCODATA_PKT: + BT_DBG("SCO packet"); + ath->rx_state = HCIATH_W4_SCO_HDR; + ath->rx_count = HCI_SCO_HDR_SIZE; + type = HCI_SCODATA_PKT; + break; + default: + BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); + hu->hdev->stat.err_rx++; + ptr++; + count--; + continue; + }; + ptr++; + count--; + + /* Allocate packet */ + ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + if (!ath->rx_skb) { + BT_ERR("Can't allocate mem for new packet"); + ath->rx_state = HCIATH_W4_PACKET_TYPE; + ath->rx_count = 0; + return -ENOMEM; + } + ath->rx_skb->dev = (void *)hu->hdev; + bt_cb(ath->rx_skb)->pkt_type = type; + } return count; +} + +static struct hci_uart_proto athp = { + .id = HCI_UART_ATH, + .open = ath_open, + .close = ath_close, + .recv = ath_recv, + .enqueue = ath_enqueue, + .dequeue = ath_dequeue, + .flush = ath_flush, +}; + +int ath_init(void) +{ + int err = hci_uart_register_proto(&athp); + if (!err) + BT_INFO("HCIATH protocol initialized"); + + else + BT_ERR("HCIATH protocol registration failed"); + return err; +} + +int ath_deinit(void) +{ + return hci_uart_unregister_proto(&athp); +} diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 76a1abb..7dd76d1 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -542,6 +542,9 @@ static int __init hci_uart_init(void) #ifdef CONFIG_BT_HCIUART_LL ll_init(); #endif +#ifdef CONFIG_BT_HCIUART_ATH + ath_init(); +#endif return 0; } @@ -559,6 +562,9 @@ static void __exit hci_uart_exit(void) #ifdef CONFIG_BT_HCIUART_LL ll_deinit(); #endif +#ifdef CONFIG_BT_HCIUART_ATH + ath_deinit(); +#endif /* Release tty registration of line discipline */ if ((err = tty_unregister_ldisc(N_HCI))) diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 50113db..385537f 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -33,13 +33,14 @@ #define HCIUARTGETDEVICE _IOR('U', 202, int) /* UART protocols */ -#define HCI_UART_MAX_PROTO 5 +#define HCI_UART_MAX_PROTO 6 #define HCI_UART_H4 0 #define HCI_UART_BCSP 1 #define HCI_UART_3WIRE 2 #define HCI_UART_H4DS 3 #define HCI_UART_LL 4 +#define HCI_UART_ATH 5 struct hci_uart; @@ -91,3 +92,8 @@ int bcsp_deinit(void); int ll_init(void); int ll_deinit(void); #endif + +#ifdef CONFIG_BT_HCIUART_ATH +int ath_init(void); +int ath_deinit(void); +#endif -- 1.6.3.3 ^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [PATCH] Added support for Atheros AR300x Bluetooth Chip 2010-03-11 13:18 ` [PATCH] Added support for Atheros AR300x " Suraj Sumangala @ 2010-03-11 17:15 ` Marcel Holtmann 2010-03-11 17:35 ` Luis R. Rodriguez 0 siblings, 1 reply; 36+ messages in thread From: Marcel Holtmann @ 2010-03-11 17:15 UTC (permalink / raw) To: Suraj Sumangala Cc: linux-bluetooth@vger.kernel.org, Vikram Kandukuri, Suraj Sumangala, Luis Rodriguez, Jothikumar Mothilal Hi Suraj, > Signed-off-by: Suraj <suraj@atheros.com> > > --- > drivers/bluetooth/Kconfig | 11 ++ > drivers/bluetooth/Makefile | 1 + > drivers/bluetooth/hci_ath.c | 353 +++++++++++++++++++++++++++++++++++++++++ > drivers/bluetooth/hci_ldisc.c | 6 + > drivers/bluetooth/hci_uart.h | 8 +- > 5 files changed, 378 insertions(+), 1 deletions(-) > create mode 100755 drivers/bluetooth/hci_ath.c > > diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig > index 058fbcc..81abeff 100644 > --- a/drivers/bluetooth/Kconfig > +++ b/drivers/bluetooth/Kconfig > @@ -58,6 +58,17 @@ config BT_HCIUART_BCSP > > Say Y here to compile support for HCI BCSP protocol. > > +config BT_HCIUART_ATH > + bool "Atheros AR300x Board support" > + depends on BT_HCIUART > + help > + HCIATH (HCI Atheros) is a serial protocol for communication > + between Bluetooth device and host with support for Atheros AR300x > + power management feature. This protocol is required for > + serial Bluetooth devices that are based on Atheros AR300x chips. > + > + Say Y here to compile support for Atheros AR300x Chips. your patch is messed up. Make sure your mailer doesn't mess with tabs and whitespaces. Regards Marcel ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH] Added support for Atheros AR300x Bluetooth Chip 2010-03-11 17:15 ` Marcel Holtmann @ 2010-03-11 17:35 ` Luis R. Rodriguez 0 siblings, 0 replies; 36+ messages in thread From: Luis R. Rodriguez @ 2010-03-11 17:35 UTC (permalink / raw) To: Marcel Holtmann Cc: Suraj Sumangala, linux-bluetooth@vger.kernel.org, Vikram Kandukuri, Luis Rodriguez, Jothikumar Mothilal On Thu, Mar 11, 2010 at 09:15:44AM -0800, Marcel Holtmann wrote: > Hi Suraj, > > > Signed-off-by: Suraj <suraj@atheros.com> > > > > --- > > drivers/bluetooth/Kconfig | 11 ++ > > drivers/bluetooth/Makefile | 1 + > > drivers/bluetooth/hci_ath.c | 353 +++++++++++++++++++++++++++++++++++++++++ > > drivers/bluetooth/hci_ldisc.c | 6 + > > drivers/bluetooth/hci_uart.h | 8 +- > > 5 files changed, 378 insertions(+), 1 deletions(-) > > create mode 100755 drivers/bluetooth/hci_ath.c > > > > diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig > > index 058fbcc..81abeff 100644 > > --- a/drivers/bluetooth/Kconfig > > +++ b/drivers/bluetooth/Kconfig > > @@ -58,6 +58,17 @@ config BT_HCIUART_BCSP > > > > Say Y here to compile support for HCI BCSP protocol. > > > > +config BT_HCIUART_ATH > > + bool "Atheros AR300x Board support" > > + depends on BT_HCIUART > > + help > > + HCIATH (HCI Atheros) is a serial protocol for communication > > + between Bluetooth device and host with support for Atheros AR300x > > + power management feature. This protocol is required for > > + serial Bluetooth devices that are based on Atheros AR300x chips. > > + > > + Say Y here to compile support for Atheros AR300x Chips. > > your patch is messed up. Make sure your mailer doesn't mess with tabs > and whitespaces. I had issues with our Exchange server for patches but that was only for receiving patches [1], but that is now fixed. I don't think the SMTP server should screw with patches on their way out. Suraj, did you actually intend for the patch to go out as is? [1] http://bombadil.infradead.org/~mcgrof/MS-exchange-sucks-for-patches/ Luis ^ permalink raw reply [flat|nested] 36+ messages in thread
end of thread, other threads:[~2010-05-21 7:34 UTC | newest]
Thread overview: 36+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-03-15 5:01 [PATCH] Added support for Atheros AR300x Bluetooth Chip suraj
2010-03-24 5:27 ` suraj
2010-03-29 9:01 ` suraj
[not found] ` <1271673889.19858.4.camel@atheros013-desktop>
2010-04-19 18:11 ` Support " Luis R. Rodriguez
2010-03-31 10:59 ` [PATCH] Added Host level support for Atheros AR3xxx " suraj
2010-04-19 23:53 ` [PATCH] Added support for Atheros AR300x " Gustavo F. Padovan
2010-04-20 10:20 ` [PATCH v3] " suraj
2010-04-20 15:36 ` Gustavo F. Padovan
2010-04-20 17:34 ` Luis R. Rodriguez
2010-04-21 4:21 ` Suraj Sumangala
2010-04-21 10:22 ` [PATCH v4] Add support for the " suraj
2010-04-21 17:30 ` Luis R. Rodriguez
2010-04-22 6:10 ` Gustavo F. Padovan
2010-04-22 6:54 ` Suraj Sumangala
2010-04-22 8:59 ` Gustavo F. Padovan
2010-04-22 9:10 ` [PATCH v5] " suraj
2010-04-26 11:00 ` suraj
2010-04-27 6:19 ` [PATCH] New Firmware for Atheros bluetooth chipset AR3011 suraj
2010-04-27 8:28 ` [PATCH] patch to request new firmware for AR3011 Chip suraj
2010-04-27 15:55 ` Luis R. Rodriguez
2010-05-11 9:04 ` [PATCH v2] ath3k: add support for new firmware suraj
2010-05-05 12:33 ` [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip suraj
2010-05-06 7:45 ` buffer starvation with multiple ACL link suraj
2010-05-20 16:02 ` Marcel Holtmann
2010-05-10 20:12 ` [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip Luis R. Rodriguez
2010-05-11 8:29 ` suraj
2010-05-18 11:39 ` suraj
2010-05-12 13:47 ` [PATCH v3] hciattach application support for " suraj
2010-05-20 13:37 ` suraj
2010-05-20 16:00 ` Marcel Holtmann
2010-05-21 5:01 ` Suraj Sumangala
2010-05-21 7:34 ` Marcel Holtmann
2010-05-20 16:09 ` [PATCH v5] Add support for the " Marcel Holtmann
-- strict thread matches above, loose matches on Subject: below --
2010-02-09 11:43 [PATCH] Added support for Atheros AR300x UART " Vikram Kandukuri
2010-03-11 13:18 ` [PATCH] Added support for Atheros AR300x " Suraj Sumangala
2010-03-11 17:15 ` Marcel Holtmann
2010-03-11 17:35 ` Luis R. Rodriguez
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).