From 34a1d171cea659fdbe0b906cfa389c24a3ec87c7 Mon Sep 17 00:00:00 2001 From: sakumisu <1203593632@qq.com> Date: Sun, 25 Sep 2022 13:59:10 +0800 Subject: [PATCH] Update rst --- docs/source/api/api_config.rst | 109 +------------- docs/source/api/api_port.rst | 24 ++- docs/source/class/class_audio.rst | 2 + docs/source/class/class_cdc.rst | 2 + docs/source/class/class_hid.rst | 2 + docs/source/class/class_msc.rst | 2 + docs/source/class/class_video.rst | 2 + docs/source/demo/audio_mic_speaker.rst | 43 ++++++ docs/source/demo/cdc_acm.rst | 78 ++++++++++ docs/source/demo/daplink.rst | 2 - docs/source/demo/img/usb2uart1.png | Bin 13107 -> 0 bytes docs/source/demo/mouse_keyboard.rst | 2 - docs/source/demo/msc_boot.rst | 2 - docs/source/demo/msc_ram.rst | 42 ++++++ docs/source/demo/speaker_mic.rst | 2 - docs/source/demo/usb2uart.rst | 198 ------------------------- docs/source/demo/usb_video.rst | 2 + docs/source/demo/video.rst | 2 - docs/source/index.rst | 12 +- docs/source/porting_usbip.rst | 2 +- docs/source/quick_start/stm32.rst | 2 + docs/source/usb/usb2.0_basic.rst | 4 + 22 files changed, 211 insertions(+), 325 deletions(-) create mode 100644 docs/source/demo/audio_mic_speaker.rst create mode 100644 docs/source/demo/cdc_acm.rst delete mode 100644 docs/source/demo/daplink.rst delete mode 100644 docs/source/demo/img/usb2uart1.png delete mode 100644 docs/source/demo/mouse_keyboard.rst delete mode 100644 docs/source/demo/msc_boot.rst create mode 100644 docs/source/demo/msc_ram.rst delete mode 100644 docs/source/demo/speaker_mic.rst delete mode 100644 docs/source/demo/usb2uart.rst create mode 100644 docs/source/demo/usb_video.rst delete mode 100644 docs/source/demo/video.rst diff --git a/docs/source/api/api_config.rst b/docs/source/api/api_config.rst index d7754fd0..836d3d85 100644 --- a/docs/source/api/api_config.rst +++ b/docs/source/api/api_config.rst @@ -1,116 +1,11 @@ USB CONFIG 宏 ========================= +通用 CONFIG 宏 +--------------------- 设备相关 CONFIG 宏 --------------------- -.. code-block:: C - - /* USB DEVICE Configuration */ - /* core */ - #ifndef CONFIG_USBDEV_REQUEST_BUFFER_LEN - #define CONFIG_USBDEV_REQUEST_BUFFER_LEN 256 - #endif - - #ifndef CONFIG_USBDEV_DESC_CHECK - #define CONFIG_USBDEV_DESC_CHECK 0 - #endif - - #ifndef CONFIG_USBDEV_TEST_MODE - #define CONFIG_USBDEV_TEST_MODE 0 - #endif - - /* msc class */ - #ifndef CONFIG_USBDEV_MSC_MANUFACTURER_STRING - #define CONFIG_USBDEV_MSC_MANUFACTURER_STRING "" - #endif - - #ifndef CONFIG_USBDEV_MSC_PRODUCT_STRING - #define CONFIG_USBDEV_MSC_PRODUCT_STRING "" - #endif - - #ifndef CONFIG_USBDEV_MSC_VERSION_STRING - #define CONFIG_USBDEV_MSC_VERSION_STRING "0.01" - #endif - - /* audio class */ - #ifndef CONFIG_USBDEV_AUDIO_VERSION - #define CONFIG_USBDEV_AUDIO_VERSION 0x0100 - #endif - - #ifndef CONFIG_USBDEV_AUDIO_MAX_CHANNEL - #define CONFIG_USBDEV_AUDIO_MAX_CHANNEL 2 - #endif - - 主机相关 CONFIG 宏 --------------------- - -.. code-block:: C - - /* USB HOST Configuration */ - #ifndef CONFIG_USBHOST_RHPORTS - #define CONFIG_USBHOST_RHPORTS 1 - #endif - - #ifndef CONFIG_USBHOST_EHPORTS - #define CONFIG_USBHOST_EHPORTS 4 - #endif - - #ifndef CONFIG_USBHOST_PIPE_NUM - #define CONFIG_USBHOST_PIPE_NUM 10 - #endif - - #ifndef CONFIG_USBHOST_INTF_NUM - #define CONFIG_USBHOST_INTF_NUM 6 - #endif - - #ifndef CONFIG_USBHOST_EP_NUM - #define CONFIG_USBHOST_EP_NUM 4 - #endif - - #ifndef CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT - #define CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT 5000 - #endif - - #ifndef CONFIG_USBHOST_MSC_TIMEOUT - #define CONFIG_USBHOST_MSC_TIMEOUT 5000 - #endif - - #ifndef CONFIG_USBHOST_HPWORKQ_PRIO - #define CONFIG_USBHOST_HPWORKQ_PRIO 5 - #endif - #ifndef CONFIG_USBHOST_HPWORKQ_STACKSIZE - #define CONFIG_USBHOST_HPWORKQ_STACKSIZE 2048 - #endif - - #ifndef CONFIG_USBHOST_LPWORKQ_PRIO - #define CONFIG_USBHOST_LPWORKQ_PRIO 1 - #endif - #ifndef CONFIG_USBHOST_LPWORKQ_STACKSIZE - #define CONFIG_USBHOST_LPWORKQ_STACKSIZE 2048 - #endif - - #ifndef CONFIG_USBHOST_PSC_PRIO - #define CONFIG_USBHOST_PSC_PRIO 4 - #endif - #ifndef CONFIG_USBHOST_PSC_STACKSIZE - #define CONFIG_USBHOST_PSC_STACKSIZE 4096 - #endif - - #ifndef CONFIG_USBHOST_DEV_NAMELEN - #define CONFIG_USBHOST_DEV_NAMELEN 16 - #endif - - #define CONFIG_USBHOST_ASYNCH - //#define CONFIG_USBHOST_GET_STRING_DESC - - /* EHCI Configuration */ - #define CONFIG_USB_EHCI_HCCR_BASE (0x20072000) - #define CONFIG_USB_EHCI_HCOR_BASE (0x20072000 + 0x10) - #define CONFIG_USB_EHCI_QH_NUM (10) - #define CONFIG_USB_EHCI_QTD_NUM (10) - // #define CONFIG_USB_EHCI_INFO_ENABLE - #define CONFIG_USB_ECHI_HCOR_RESERVED_DISABLE - // #define CONFIG_USB_EHCI_CONFIGFLAG \ No newline at end of file diff --git a/docs/source/api/api_port.rst b/docs/source/api/api_port.rst index 1fe9716b..95be593d 100644 --- a/docs/source/api/api_port.rst +++ b/docs/source/api/api_port.rst @@ -121,7 +121,7 @@ usbd_ep_start_read - **ep** out 端点地址 - **data** 接收数据缓冲区 -- **data_len** 接收长度,原则上无限长,推荐 16K 字节以内 +- **data_len** 接收长度,原则上无限长,推荐 16K 字节以内,并且推荐是最大包长的整数倍 - **return** 返回 0 表示正确,其他表示错误 .. note:: 启动接收以后,以下两种情况,会进入传输完成中断:1、最后一包为短包;2、接收总长度等于 data_len @@ -218,8 +218,10 @@ usbh_submit_urb uint32_t actual_length; uint32_t timeout; int errorcode; + uint32_t num_of_iso_packets; usbh_complete_callback_t complete; void *arg; + struct usbh_iso_frame_packet iso_packet[]; }; - **pipe** 端点对应的 pipe 句柄 @@ -230,8 +232,10 @@ usbh_submit_urb - **actual_length** 实际传输长度 - **timeout** 传输超时时间,为 0 该函数则为非阻塞,可在中断中使用 - **errorcode** 错误码 +- **num_of_iso_packets** iso 帧或者微帧个数 - **complete** 传输完成回调函数 - **arg** 传输完成时携带的参数 +- **iso_packet** iso 数据包 `errorcode` 可以返回以下值: @@ -258,4 +262,20 @@ usbh_submit_urb * - EPIPE - 数据溢出 * - ESHUTDOWN - - 设备断开,传输中止 \ No newline at end of file + - 设备断开,传输中止 + +其中 `iso_packet` 结构体信息如下: + +.. code-block:: C + + struct usbh_iso_frame_packet { + uint8_t *transfer_buffer; + uint32_t transfer_buffer_length; + uint32_t actual_length; + int errorcode; + }; + +- **transfer_buffer** 传输的数据缓冲区 +- **transfer_buffer_length** 传输长度 +- **actual_length** 实际传输长度 +- **errorcode** 错误码 \ No newline at end of file diff --git a/docs/source/class/class_audio.rst b/docs/source/class/class_audio.rst index a9f00098..d1a9e207 100644 --- a/docs/source/class/class_audio.rst +++ b/docs/source/class/class_audio.rst @@ -1,2 +1,4 @@ UAC ========================= + +参考官方 audio 相关 pdf \ No newline at end of file diff --git a/docs/source/class/class_cdc.rst b/docs/source/class/class_cdc.rst index 1ee9a86a..d3ed560f 100644 --- a/docs/source/class/class_cdc.rst +++ b/docs/source/class/class_cdc.rst @@ -1,2 +1,4 @@ CDC ========================= + +参考官方 cdc 相关 pdf \ No newline at end of file diff --git a/docs/source/class/class_hid.rst b/docs/source/class/class_hid.rst index 21b85cf2..0f9ef13d 100644 --- a/docs/source/class/class_hid.rst +++ b/docs/source/class/class_hid.rst @@ -1,2 +1,4 @@ HID ========================= + +参考官方 hid 相关 pdf \ No newline at end of file diff --git a/docs/source/class/class_msc.rst b/docs/source/class/class_msc.rst index ab230e70..c01a359c 100644 --- a/docs/source/class/class_msc.rst +++ b/docs/source/class/class_msc.rst @@ -1,2 +1,4 @@ MSC ========================= + +参考官方 msc 相关 pdf \ No newline at end of file diff --git a/docs/source/class/class_video.rst b/docs/source/class/class_video.rst index 7a1458ce..cabe5173 100644 --- a/docs/source/class/class_video.rst +++ b/docs/source/class/class_video.rst @@ -1,2 +1,4 @@ UVC ========================= + +参考官方 video 相关 pdf \ No newline at end of file diff --git a/docs/source/demo/audio_mic_speaker.rst b/docs/source/demo/audio_mic_speaker.rst new file mode 100644 index 00000000..3c1d2ca9 --- /dev/null +++ b/docs/source/demo/audio_mic_speaker.rst @@ -0,0 +1,43 @@ +USB 双通道麦克风和扬声器 +============================ + +软件实现 +------------ + +详细代码参考 `demo/audio_v1_mic_speaker_multichan_template.c` + +.. code-block:: C + + usbd_desc_register(audio_descriptor); + usbd_add_interface(usbd_audio_alloc_intf()); + usbd_add_interface(usbd_audio_alloc_intf()); + usbd_add_interface(usbd_audio_alloc_intf()); + usbd_add_endpoint(&audio_in_ep); + usbd_add_endpoint(&audio_out_ep); + + usbd_audio_add_entity(0x02, AUDIO_CONTROL_FEATURE_UNIT); + usbd_audio_add_entity(0x05, AUDIO_CONTROL_FEATURE_UNIT); + + usbd_initialize(); + +- 调用 `audio_init` 配置 audio 描述符并初始化 usb 硬件 +- 因为 麦克风+扬声器+控制需要 3 个接口,所以我们需要调用 `usbd_add_interface` 3 次 +- 默认描述符中开启了 mute 和 volume 的控制,所以需要注册对应的 entity,使用 `usbd_audio_add_entity` + +.. code-block:: C + + void usbd_audio_open(uint8_t intf) + { + } + void usbd_audio_close(uint8_t intf) + { + } + +- 当我们打开 PC 的音量图标,或者音乐播放器、麦克风界面时,会调用到这两个接口,用于启动或者停止数据传输 + +.. code-block:: C + + usbd_ep_start_write(AUDIO_IN_EP, write_buffer, 2048); + +- 由于 audio 协议中没有应用层相关的协议,传输的只有音频的原始数据,所以直接调用 `usbd_ep_start_write` 即可,发送完成会进入完成中断 +- 由于扬声器需要使用 out 端点,所以需要在 `usbd_configure_done_callback` 中启动第一次接收,当然如果没有能力接收,可以不启动,在想启动的时候启动 \ No newline at end of file diff --git a/docs/source/demo/cdc_acm.rst b/docs/source/demo/cdc_acm.rst new file mode 100644 index 00000000..3f6629d2 --- /dev/null +++ b/docs/source/demo/cdc_acm.rst @@ -0,0 +1,78 @@ +USB 虚拟串口(无 UART 功能) +============================ + +USB 虚拟串口主要是借助 USB CDC ACM 类实现,将其模拟成一个 VCP 设备,当插在电脑上的时候,可以显示成一个串口设备。跟市面上的 USB2TTL模块的区别在于,虚拟串口仅仅只使用到了 USB ,没有与串口(UART外设)进行连动。 + +软件实现 +------------ + +详细代码参考 `demo/cdc_acm_template.c` + +.. code-block:: C + + usbd_desc_register(cdc_descriptor); + usbd_add_interface(usbd_cdc_acm_alloc_intf()); + usbd_add_interface(usbd_cdc_acm_alloc_intf()); + usbd_add_endpoint(&cdc_out_ep); + usbd_add_endpoint(&cdc_in_ep); + usbd_initialize(); + +- 调用 `cdc_acm_init` 配置 cdc acm 描述符并初始化 usb 硬件 +- 因为 cdc 有 2 个接口,所以我们需要调用 `usbd_add_interface` 2 次 + +.. code-block:: C + + void usbd_configure_done_callback(void) + { + /* setup first out ep read transfer */ + usbd_ep_start_read(CDC_OUT_EP, read_buffer, 2048); + } + + void usbd_cdc_acm_bulk_out(uint8_t ep, uint32_t nbytes) + { + USB_LOG_RAW("actual out len:%d\r\n", nbytes); + // for (int i = 0; i < 100; i++) { + // printf("%02x ", read_buffer[i]); + // } + // printf("\r\n"); + /* setup next out ep read transfer */ + usbd_ep_start_read(CDC_OUT_EP, read_buffer, 2048); + } + + void usbd_cdc_acm_bulk_in(uint8_t ep, uint32_t nbytes) + { + USB_LOG_RAW("actual in len:%d\r\n", nbytes); + + if ((nbytes % CDC_MAX_MPS) == 0 && nbytes) { + /* send zlp */ + usbd_ep_start_write(CDC_IN_EP, NULL, 0); + } else { + ep_tx_busy_flag = false; + } + } + + void usbd_cdc_acm_set_dtr(uint8_t intf, bool dtr) + { + if (dtr) { + dtr_enable = 1; + } else { + dtr_enable = 0; + } + } + + void cdc_acm_data_send_with_dtr_test(void) + { + if (dtr_enable) { + memset(&write_buffer[10], 'a', 2038); + ep_tx_busy_flag = true; + usbd_ep_start_write(CDC_IN_EP, write_buffer, 2048); + while (ep_tx_busy_flag) { + } + } + } + +- `usbd_cdc_acm_set_dtr` 函数是主机发送流控命令时的回调函数,这里我们使用 dtr ,当开启 dtr 时,启动发送 +- `usbd_configure_done_callback` 是枚举完成的回调函数,因为 cdc acm 有 out 端点,所以我们需要在这里启动第一次数据的接收,当然,如果你现在没有能力接收数据,可以不启动。 **数据长度需要是最大包长的整数倍**。 +- `usbd_cdc_acm_bulk_out` 是接收完成中断回调,我们在这里面启动下一次接收 +- `usbd_cdc_acm_bulk_in` 是发送完成中断回调,我们在这里检查发送长度是否是最大包长的整数,如果是,需要发送 zlp 包表示结束 +- 调用 `usbd_ep_start_write` 进行发送,需要注意,如果返回值小于0,不能执行下面的 while \ No newline at end of file diff --git a/docs/source/demo/daplink.rst b/docs/source/demo/daplink.rst deleted file mode 100644 index 7fd292cb..00000000 --- a/docs/source/demo/daplink.rst +++ /dev/null @@ -1,2 +0,0 @@ -DAPLINK V2.0调试器 -========================= diff --git a/docs/source/demo/img/usb2uart1.png b/docs/source/demo/img/usb2uart1.png deleted file mode 100644 index 09822f158e6d6e977759cf95cbf1ef988804094c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13107 zcmd_Rc{r5s|2KSS zMqw!Xz7J;XW-P;SU;6%j$M3$MKknmr{&@a+{<$1;nd>~S^S!)YulM^r=kPO?7^~eE)TW=pFGRt9oBP+nd!#iMd@$5qSH4v+y?~h&o;swa&Wi?0 zvnA&hIBo(--cWy&N3We%e`$Xux6$R%5L80s9+xA0opjy(#Zk|$W#@3Fc=*z6LOn@Q zrk*{4+kEW7OfajdJ>OTfnjO}5;nwnj>~?-cSM89J`_QAn zrLM1NU4~1vYbu6Q^NCT ztg)&0Sr<)_Prp;s2p}Ma z$WAqtWP=V&t!;I6wG!~GkK4Xnh=?BrrVJGC(bz9;GRG~ptH^wN_oj5Zi(3D@We4VazhhnmDG zCcGG~eG5E*yD^CnO$FoumbF@C?zKa6YqSl@=J}Iopo?-$4Zn8bqlNnU3ZU=3#5<_u;n47|-hll-CdVp21sYvH5|555+bCr_jRFi#xy3 z%exaqsdfInB4}hqZUu!b7*Rl*Ib>ZiU)=I$&u@mOH&lcV^(3j4Hw#zAYx%QH0GwWo zxOdoeSId%KJi+I;Ai0Yz@hTylo&O~$jP@OBY{XZ;naFmNXtMIxSs8n&d>Te}`tVV) z0e9n8?71+~aZLkl>?`MiKa48?xb{+VH@$46XQGJ(OFu3v@4y>>=!20!Jme|v?EI*E z?dC0O)uPtdq=3SI-sFFNJx_Lh6LF9*m+f}1(U-wy331emJr}L1v7^ADaq*LgQMz^% z*M9Uu!>dtlwM8jQ&ucgM20xw4x+_;M!>7#W$5$McK*<%%dmcA6*yKSJJoo^%l26`CwGd#8a zEj=^ruHX_riD5i@kc7&Q74!*SKl5?{m>?_2eeY|bKb5G>_fmL=SpUnX$`{eywv9T>a5cOyLS+;s9i#+k2dAv^BnYEn&n2-s=5Xts#S8QSsg=rDeneG zDS*nA?9kg3rGL>i765=VTwn$qF(e;p+d~~lxz+f8r50V}f!VH1o$!Yq2ZK*$O|XHa z^m6R~^)+LIfat$osc69yYy-yWtRjaLJCAV6koNZo2zK{T9J?$6e9xEK&kl?={CoGn z6MJXppK6%x^@RsCZ>W#SXT2aqb@{cN&R!d`;3_Iux-l>)>=lSBYHMq2WM@kEa2w?s ztU@lFu|klHkvTKBRzczNzor%MPTZo{u2I@(L>)Vw zV-I+N_hp?d7+Xcq5MU~t+yEWpy8aR30>bNh~v zJqJR?x8SF*-+IQ%hkF0f*9545=e7$o8PSwH&#!YokFRSKkhYKmghM(}s*m36W$` zz0q+~gOY_5w@Hh&?~zXjH1_Raa`qPYmd;(l17WFtnCa26$-Ht~I7qX>&sRg^#p|0) zML+&A=E*Dy072S1!=#aYYZZ!ajhLWTxWh5vJ$lsqiB`cL*L=>bq#uV^&jzORH!Hq# zW#*g(xx(_DNDzykH?Zl{d#Bq!t)QMg5D|AfDY)#+9yW@%Iu<7n1ZLlUDT? z)n6>B>1~*6Qyzs|>M5*;slZc(w4zh+WcT&SA_JH;O4#A0~H{OS!+RJW{! zyqX+C{(jzsyCl4k_ch+@!g?U*18g%@dY_gi zVlofh39}=yUHRR07+{mIY2a6ZPhw~Xm{D(d0BfP7+&_cG_7UC#^)K1ghThqLGGbQ9 zJG_HpsMXVEXphkplHch-C}LJsOG8B<>1`cdhcn`*NLIt>J^97E+=Cr)I4zyoxu~;V zM>X2O6$AB0HHM9C!aF*-ld6E@QI?noYR5`A?up=WJ-Ks)EuEhJav{@Mo26O9QHYM} zJ{VoL`ua5VWG||9%Z=_2r}1!@VKTV??G5K!JMj$pB*86k6cua&>JrpXfvefWqvB6k zs_R>xhk1H47c*N(ctO3_V7%PV7J5N7c?C@E2ait`zKLFtcg*?e2ZlSy1oU_LSnSe} z1WLkwaPkC{RxV^~G<)*{nATm%e}SRanC}w2(?aOIU9Uj(E`DL8w)&Bo*`4*BR}w+; zKKp>nc3o359mN~`<*!${g>Jmx|KLgae?rUEYu7%H9J+J~Q{%Kq-;c6yS7l^!z&qb! zY;3?q$ZADIMC2(SIGL0d7`z6A{`Kke%F4>eEuKv?D*GUH{pMcE++?owK3wf*+93xA z2XQP62IF<-h~@`tkRyiLSYh8*8*6R$@E|NMF%)Yodo zIvpOJ=(L2D%6AUeoJSriCsi`*`P@NA{k6%?X1=-831_LVxk5%(2ctRFsM&r#w?|r| zb!MHTz$DtR{m#>&EIEGyH2?;xtR6mF7Nhd1vQ-JYBzNF=r`D`9Pipdk z9!+2sl(`)Nsv%sp1F;S389^1lP|Ex6zEjxI#~kjHRB`Y&L3k(7{973(Q72q&Y(yF2 z`?Z+XQpll*=-4`FfrPJ6<)O6UYCi!fDXBbVwv$sHVf%i!)}qYJ7<(LyR@{e~=Ks)W zw4$N)(f^^*{d|o6{P}ZNa&X8etUz08An1cL3DeWl;GIJ%*RLMmx44t1ryN84f_7&o z&r{)yr+?iftE>!jRV8np-{%}H@!52F%=zxa|249h+}{!@c{~(idpPK*WZ~P{ zLi$~X;XsIVf+(0>=l9oNB>ycsuU$EP&9{TZ{%tJxO*HCD1x^t!^v2b!*KV{bSbP&+ z2EJW}Un|JZ-oe89tv0>S22t=elTcA$D>LtymP{pc^V2{a#00JKNeIL)Eh(>~)&LF! ziwr+6SHnNp>n(1B9U^f1T0wBpwUe~TtZ<%DG!ddLx>k`E-tqDV2vrX~zB~O5DH-d8 zoB#N3bxdogNre92{So~8XWorYf=)O$m@gMx|HpEm!1@2kCv3{{^1KS%XTZ`0cnjS8 z8;bkx0yT&1!TkJu`)=JuH~O3CB;B^c?75Z!if%P1nI5k+pB++BT<%p46+rOaNIPnlIa!YUd` zu^;q*>ltoqia|6KwXk8pWbC*{{HERLaLrs+j?_rj>APtK!qV|K{Wggw4rzf^d9dxm zZ+Sb!t~Tnw9QShK#>Q(8OW59p45?$sj^!!aDC`GUj%11n(pDJN(O(+Y=#C z|A91c4bPJk6FJVYzdZmZ;O*cHwiFO2_NN17z?tXW-QC4hu?;2Ezj|$16j&$S-fb<* zf;v&Dq*FzN_wFx_M9_&Adj584n_INeRhBIVf(V#T7Fl?Z?mYOcYrl1I53{w>e&sC` zih;NEbvWhGWl;w5%%ZsE-sCI%;7Q(Z~_GOUEsZ(&#?76jRW}Vmn zxUWsA_TtrG&--dON~}mLlce6Eu2MTv)Q56VIKD4;O!LovM$OB4NIV1xud>ja=^Z+$2v@;ypt|Q|wL7e0Le_rv zFED6tv4-k`_LA;mhmt=CDSlx;uXvT-krn2BU2UIG1p&I2AfL#UtnzxkQ;5`8`EiWH zQ5{H=f?lMGS2K#*2gc=1<*>k*Ix`mm%V#!4Zi9qz82X`ymXMm?;??!}e)+Q_HU8e; z3lbp=RD4|ALl4f|UyS(DyUsS_4r`2uiTn%UcS!#I(k$QHA=P8ewt zPuEyeY+yU1m|1-?@Pcu=3%cpp|}ech#8V4iO@(?K<{rC4NgIzZ&}Y=sB8)3#2}6j#U9) z-TKPvECkR915?Q#xyzHsR4FxiefXpyhuK_Cc~Mak66y5)B#_|>tDU!RTwfS$fk3W= zn4kyxH1Qk|hfva!{LS1g#`t;W)|9EeoQ@$b|2i9Boh1p;oLz?B#~%!K6SgXUpMb%Y z<+boR`4Fs6pNb0;FHL=*si7gt4n${pEcE@JTT9&T=+F+XY>zbaHCXm!6#XWJ)7MrC zq(Y;={k~l-3ILeP1{QfH3omuH`@I_z%tkK3QW*&3=-F+ z`c|XcADvx4uX#(t0noCl)%g9ug0LuDbU3sFgJ(ST9lw!Pa!=1Hz3O=`9!FFlILkR& z@)IxJ1r7rA&>L8zTr6hZZu%z&gZB()Z(@oYJT;ENDw5eTLB1g*B10qbI=>W5K9IC? z8u27WqgLhtVD@2aVzTCDJrABkohYxo{Q`T3M~$#TBoSC0%w%TPZcUR-pq##~{bwPDyX7_k*qvYeyk$+x=9s)!MayQj?mK$DM{W}6iZaj*& zSX|9*OTDs#>sujCWnF zjGF)oXvsE6&aS!KaCy`yZ+24>f{$v=D%d>nQT7tGS~T5NGRp*A^@A*UFIec|L_Y1H_1nj9yvMRYnn{)Cg*+GZ05-w0xo>cJ0x~EFv8*ZMS!WOc& z2{aF1c!_=AZDKY8we3`M8Hg+4f<+c4g}F!8I!hj&OP+9{zB{GLmyS;CRk>amxF9He zV>Cd1VMeHPdx+0tdBtL0YDP0`XBG#1WY56KPMKpp(0W>&{)PvQHGiaS-@S30_j|Pb zAa{@7gXWZ=y+Qu95xPZpV)8T-ZUotSGL4!-Yhg9kA7#0%)RLH`(REl=b=T_CHPTZq zxeyBXDE}do=w+^ad+mnjQeXPA=V*k(rw0jL99M|;UbLOVbsdH~xLA5=C|be-c9T0% z;DpZbIO!ZUU`)+>4R2BXvB!I{a)v)>Z+o<$(@x5X*q*xZYk{JId9>)r=;~53?~HM+ zk@ERr>Wmm1JLh=pqUiG`zF+9}*W(;>KbB}t1w?Pr(VL9WGL4>@Je0I|s@6Z+*wlh& z8B0$XEBGzUWVKJfeLSC<@BCL`@}e4F<1wpEZQACH#*#m+DG_GnAr=sF+KoN~#z?2b z1(!eXCpm8`Ieg7?S-jqB-YwH?YM@Q@DnQI?zGbS|#Cf@`kxZU?c@DD5`S!MdXj=aR zH;^Qd@Zt%g^h_qrYVWbXO7)WRFQvA1wb^8=kR53hNMj?<8js(BcAtTDCPi3g;y9m# ze54}|=tQ<#wDMRxHA;wIV2o>MDO!Ti1yEh6#fc6Z=UA#}TKSQ5V*HSP5zE8&Wkg}#+Y|4x@Ssu)%|lQt%ibGa zJG;9rd#v~g-93hGX-t=i0m3dzSrd+i*%XXrhtFG-_} zx`t?i$3d=Og2mJN!;Pc6>*W(QYh)=S>qnQx11gdT^wFA*ZKj_-N?V93 z3#j7mktfAIVX;04emw6P<}UBdc6;*Dj{=7?e{^Ch$+HpfSM6R6UAuZ!AIb7sC`eIY z8Lp-txj?=_`!cb!)}EeHY4!pu90+e++th0$`5tUIQN+qabkZHJCHEAP(>nJ;tZ9Ql{b_3Z@}%-(pF>;}-7ouoVZ zk@M9T_VV`uHDjJ~rt+noRn0{vT|^TpollJa@w~|Ma54>Fji2xPf`c&aV0^jmfJ>4N zE^59^xVsFLpSO+P)V!5zZ93<*X;8neqkHt=s^c+f!d$1)-}Z$0p8ndUumTY>gx`EU z?U|S#V`fLfpaGFu!XjY8#&E|vL(>x4%k^LNiHTY)wBAi#ie1wF1dY#;GxBlbZu!~V zb!8Tb`@Z&3pw_AFfjPcdC0KpfchIfT5nRpcic#Pyud8=livn@9lloGB**U{#TwV57YQ?UrWz4~5##RHrJ2?^xs?j8*}+RV_pKlsm1-X06{=o6c$+Z>4w7iL^i*%|B_d(26t{X725sD-fzGH zrKxhHV<`|8{*xDd?%lGO3Gj}4LRI*R0X`8vy)Q;|gx*S$h|MP$wWYsUsC|p6v;A%X?fkSw9FShmVQ%X6JrKBP zGNm1QgB29G$|l*n9V22Yk|;gx%PMj^>n%LEDoW#-Hg`Nax9u|?Dqu*qWW9^8`BZcI zn+}hLiDA6+>Z5dBjkvATir8HhpFj)*Q=?i)v@BaFu4TH$WRqK@s}XIhjBPZx~Yx@g`Aw#p%D*5MzKPP55v#=g5BtGbxcWPC&;BoHR+ZN-xJ}>b9N8Y(h@-`l<3=}|l*QL-XckM7 zBWr0J@3Hnts4rpA8GQk;R!AYbvnrD+K$$C!WxOd;U-C}h9-8t8()CS%sOBwyYo- z^i;rSnD^B7I%_GY4l(dvO4NZ_nJUCmrXmRM0dqN6em7 z4HrH+$WA(bj*x2ihV!1DbxE6}>q9E9x&1%cg{IwAItTH0kWYzYFOG@BoO{}*#iqjP z<#jB#emWz{eewmpKj2VDs_)3as7rixHjUENjo!Yg2{OjJMEe7kM(9QWmRH}_bY`~~c^N5L+I zx`dasp`f%iB`CBgj0K_B+J&iFz?@HlK4LtIGiIR{Yd1!IcK2Ce&}7w_6eKM!B8f_O zkY;Tm<)9aO1GC9X&+ae3&g&{|lCD=v=$uXbOS z4w;A3BQD?q`bB5yB4&(7cj@UVn4<)KNgH}rYyZg8ehI^W=dGsq`?Ch=!+UZB=^VIZQ7 z%?56eZ9GLM|x*eDJ7rl9M-Bc`nc6Q+S|p-dx`61_I##GC3YiSs}5^%H{M!E z-u~lvXGAQIQdRnUNn(9tM9HIIjQ7-S1m8I;KHZ+_d}?2|f!gu8ZeiHhxqD+Lk!;#d zqp8tZO}07JaYe{)k!I2YMT4h}yE!d-l+(>tVqCB|rk-`*@Iuo0Hyk!;j zkJn+5vd%v&u)T6^X*%3oBQD@9;hLBlyJGrBnf%$7JjUEwAEEJIBsDIe*0{_BhFHs8 zaRC9(kGPf^>)*B6w`I|8Kgf+q^uW-}?MqT2={sC@BjMRy>$B~10bhc+yG9KczO^wz zb}-%Bf2NRwmM`7X5*Sw~j(v^YR=Pc(vGOwiW^Qmi-1zgrTw47LD8sA)0}`31QK#2tbMA|_bU9PsXkf9GtUp%uO^#qDkAcQM+23f-o00u zG`xjIEPNzdpX9x-ojpj&FEQ->Z9#14&lm3H(D%Ac$?nxE5o&S|2ts$#k=_2gxE11t z+3d{915E^v=Fak^islMUu=5c6D=KAZnI~wo;YnhIbd^)F^xO_&eIX{m`BVu@mi9!# znrye*Olv4MteopwrEve-=18{sm$|XuaX8LpwKE|QArrLKLiV42bv_XmD#m!^7IEW+ zZei08vIqA)+3V%KgtyWD45W|6id@4ZA%{lN`bHNiu``+I-s}2`1#i4+XZq-gs(fw& zpU~h|0DWt!Wb6jQLc{bq3HDp=WwgKHSHfIfqVe8T&s-MoAASBRIPWQI+o;)JAI@Lg zscgr%U;lzv{EY4V>Er!)o{`tZq+J!La?dQZ5}84{DIxK--AAmVUvs=7`rAtSWVU}M zPiO7jpTl5$DA+-fy-m_j^y;MKUcN#y0g;*S5+8a04@kHq3A}WcvsAQ=l2^|}OWRTzZ!44yiVrkdE;=4&8l@*m^f zC7k-}=b$2MRq(syHaK4X{YA1uT?l59S#*7Sx=;J6O2( zpC8oLQOR5mfk$Gl!s{EM)4xSv(BPvtztJls)uQ|uhCFxLnqJ&b7dzvwOr7K znYx(|PwGbXXk$=8BCtAHF@XpBnWNqa*l}G9x64^mKDK8=A?kbj& z!f#c@p$al0@ku5I?%78gl3uIC3|cUf&~O24sfDw7&=-)d`?Mp4zaVJozTH2w+YL;J z`EknrBK;3w-l4Q;{{vHYPNVI`6Lx8NrrZnAn_7#?9wYFm&Y4*8e>hIKR8_(S=nHb` zgXP1NV#0K?Y(q@`-6y86=v!```QQJPnkYV)hBKZ*X9f_G3fntVJAt20uTK8lKH}r* zlqvq>Q(nvbJ+aK2k)1hP6l0sLmOxdecm4&3LDNB{Ez5|=0!}+a0ep6TNA^|N<)39G zi<0y{x{ZWKSh|v=24Q??G#qkf5(^&ab#n>`%~KQ0r*r7P zh8zaJeWS3SA8sot2Tgr%(e4cPE|YN;FT&l>vlTaJM}Ulbk>p7`Uva~{2)EZ7hlVd_ z$9gs2aQ+SsqMtRE74_*)c=eNYnIzw`pNPO>p+@_tlAi^!tIYLk3pqkxNH2qdn#L&f!$Y|89{T1TJ{nH%d&MSH^o1SHw$n zJdKge3yo1UHIAge=NYniRV^upfarL`!fwSM)-ff7m*U{AhGVTMc$)CriMEG)007v@ z-CN@Y4;MxlZf|B-W&0183@r>*mh_c48d2K-p!Dq+TVc`pli6xd$F!&h0HA*-Yixg0 z$F>)55lP`O3x{A`1J8p~*N#sUqaPhq?ABO!6iG{20vOX(pQtBN~uDxgRfl zV!UZq@Eu~VCA8*)!t^oq5Mdy=<B+}fkHFCwLx(e~4EZ-4`W952#{(EW zSZ;wLt}*YkRRaVECaXSM9&@2aYNbt4=e=V7+~}oJF9!FeWkt?dJbo-07auPHbA&a3 zCz`kj{6I7to*b1|tf*5a6ozQGiOM>72sl9=lV703B{0erJ!VmewaPdyjqmw$O5LfN zhhei$v{%x+;ObdQVrVY`ZqZ}d> z(M1adOdcsj1PFHaLi)lsSBee`j;dIs>U3hzy1Jn$z;C6(rI08nEgvN$msw1m3Z<-n zdSVJU+5AmHuGC0AJqiBGq)KsSazsF6`s?m>}A&%xN%w~mp z*&kDPnC(a=CLMYg&E3X)20o#svc0K)Pf*Rz>aqmIUz>TUUpPD@4o7a4