Library for Bert van Dam's book "ARM MICROCONTROLLERS" For all chapters with internet.

Dependencies:   mbed

Committer:
ICTFBI
Date:
Fri Oct 16 14:28:26 2015 +0000
Revision:
0:4edb816d21e1
Pre-update 16-10-15

Who changed what in which revision?

UserRevisionLine numberNew contents of line
ICTFBI 0:4edb816d21e1 1
ICTFBI 0:4edb816d21e1 2 /*
ICTFBI 0:4edb816d21e1 3 Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
ICTFBI 0:4edb816d21e1 4
ICTFBI 0:4edb816d21e1 5 Permission is hereby granted, free of charge, to any person obtaining a copy
ICTFBI 0:4edb816d21e1 6 of this software and associated documentation files (the "Software"), to deal
ICTFBI 0:4edb816d21e1 7 in the Software without restriction, including without limitation the rights
ICTFBI 0:4edb816d21e1 8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
ICTFBI 0:4edb816d21e1 9 copies of the Software, and to permit persons to whom the Software is
ICTFBI 0:4edb816d21e1 10 furnished to do so, subject to the following conditions:
ICTFBI 0:4edb816d21e1 11
ICTFBI 0:4edb816d21e1 12 The above copyright notice and this permission notice shall be included in
ICTFBI 0:4edb816d21e1 13 all copies or substantial portions of the Software.
ICTFBI 0:4edb816d21e1 14
ICTFBI 0:4edb816d21e1 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
ICTFBI 0:4edb816d21e1 16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
ICTFBI 0:4edb816d21e1 17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
ICTFBI 0:4edb816d21e1 18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
ICTFBI 0:4edb816d21e1 19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
ICTFBI 0:4edb816d21e1 20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
ICTFBI 0:4edb816d21e1 21 THE SOFTWARE.
ICTFBI 0:4edb816d21e1 22 */
ICTFBI 0:4edb816d21e1 23
ICTFBI 0:4edb816d21e1 24 #include "UsbDevice.h"
ICTFBI 0:4edb816d21e1 25
ICTFBI 0:4edb816d21e1 26 #include "netCfg.h"
ICTFBI 0:4edb816d21e1 27 #if NET_USB
ICTFBI 0:4edb816d21e1 28
ICTFBI 0:4edb816d21e1 29 //#define __DEBUG
ICTFBI 0:4edb816d21e1 30 #include "dbg/dbg.h"
ICTFBI 0:4edb816d21e1 31
ICTFBI 0:4edb816d21e1 32 UsbDevice::UsbDevice( UsbHostMgr* pMgr, int hub, int port, int addr ) : m_pControlEp(NULL), /*m_controlEp( this, 0x00, false, USB_CONTROL, 8 ),*/
ICTFBI 0:4edb816d21e1 33 m_pMgr(pMgr), m_connected(false), m_enumerated(false), m_hub(hub), m_port(port), m_addr(addr), m_refs(0),
ICTFBI 0:4edb816d21e1 34 m_vid(0), m_pid(0)
ICTFBI 0:4edb816d21e1 35 {
ICTFBI 0:4edb816d21e1 36
ICTFBI 0:4edb816d21e1 37 }
ICTFBI 0:4edb816d21e1 38
ICTFBI 0:4edb816d21e1 39 UsbDevice::~UsbDevice()
ICTFBI 0:4edb816d21e1 40 {
ICTFBI 0:4edb816d21e1 41 if(m_pControlEp)
ICTFBI 0:4edb816d21e1 42 delete m_pControlEp;
ICTFBI 0:4edb816d21e1 43 }
ICTFBI 0:4edb816d21e1 44
ICTFBI 0:4edb816d21e1 45 UsbErr UsbDevice::enumerate()
ICTFBI 0:4edb816d21e1 46 {
ICTFBI 0:4edb816d21e1 47 // USB_INT32S rc;
ICTFBI 0:4edb816d21e1 48
ICTFBI 0:4edb816d21e1 49 UsbErr rc;
ICTFBI 0:4edb816d21e1 50
ICTFBI 0:4edb816d21e1 51 DBG("Starting enumeration (m_pMgr = %p)\n", m_pMgr);
ICTFBI 0:4edb816d21e1 52
ICTFBI 0:4edb816d21e1 53 #if 1
ICTFBI 0:4edb816d21e1 54 m_pMgr->resetPort(m_hub, m_port);
ICTFBI 0:4edb816d21e1 55 #else
ICTFBI 0:4edb816d21e1 56 wait_ms(100); /* USB 2.0 spec says atleast 50ms delay beore port reset */
ICTFBI 0:4edb816d21e1 57 LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS; // Initiate port reset
ICTFBI 0:4edb816d21e1 58 while (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRS)
ICTFBI 0:4edb816d21e1 59 __WFI(); // Wait for port reset to complete...
ICTFBI 0:4edb816d21e1 60 LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; // ...and clear port reset signal
ICTFBI 0:4edb816d21e1 61 wait_ms(200); /* Wait for 100 MS after port reset */
ICTFBI 0:4edb816d21e1 62 #endif
ICTFBI 0:4edb816d21e1 63
ICTFBI 0:4edb816d21e1 64 DBG("Port reset\n");
ICTFBI 0:4edb816d21e1 65
ICTFBI 0:4edb816d21e1 66 wait_ms(200);
ICTFBI 0:4edb816d21e1 67
ICTFBI 0:4edb816d21e1 68 m_pControlEp = new UsbEndpoint( this, 0x00, false, USB_CONTROL, 8, 0 );
ICTFBI 0:4edb816d21e1 69
ICTFBI 0:4edb816d21e1 70 //EDCtrl->Control = 8 << 16;/* Put max pkt size = 8 */
ICTFBI 0:4edb816d21e1 71 /* Read first 8 bytes of device desc */
ICTFBI 0:4edb816d21e1 72 rc = controlReceive(USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, GET_DESCRIPTOR, (USB_DESCRIPTOR_TYPE_DEVICE << 8)|(0), 0, m_controlDataBuf, 8);
ICTFBI 0:4edb816d21e1 73 if (rc)
ICTFBI 0:4edb816d21e1 74 {
ICTFBI 0:4edb816d21e1 75 DBG("RC=%d",rc);
ICTFBI 0:4edb816d21e1 76 return (rc);
ICTFBI 0:4edb816d21e1 77 }
ICTFBI 0:4edb816d21e1 78
ICTFBI 0:4edb816d21e1 79 DBG("Got descriptor, max ep size is %d\n", m_controlDataBuf[7]);
ICTFBI 0:4edb816d21e1 80
ICTFBI 0:4edb816d21e1 81 m_pControlEp->updateSize(m_controlDataBuf[7]); /* Get max pkt size of endpoint 0 */
ICTFBI 0:4edb816d21e1 82 rc = controlSend(USB_HOST_TO_DEVICE | USB_RECIPIENT_DEVICE, SET_ADDRESS, m_addr, 0, NULL, 0); /* Set the device address to m_addr */
ICTFBI 0:4edb816d21e1 83 if (rc)
ICTFBI 0:4edb816d21e1 84 {
ICTFBI 0:4edb816d21e1 85 // PRINT_Err(rc);
ICTFBI 0:4edb816d21e1 86 return (rc);
ICTFBI 0:4edb816d21e1 87 }
ICTFBI 0:4edb816d21e1 88 wait_ms(2);
ICTFBI 0:4edb816d21e1 89 //EDCtrl->Control = (EDCtrl->Control) | 1; /* Modify control pipe with address 1 */
ICTFBI 0:4edb816d21e1 90
ICTFBI 0:4edb816d21e1 91 //Update address
ICTFBI 0:4edb816d21e1 92 m_pControlEp->updateAddr(m_addr);
ICTFBI 0:4edb816d21e1 93 DBG("Ep addr is now %d", m_addr);
ICTFBI 0:4edb816d21e1 94 /**/
ICTFBI 0:4edb816d21e1 95
ICTFBI 0:4edb816d21e1 96 //rc = HOST_GET_DESCRIPTOR(USB_DESCRIPTOR_TYPE_DEVICE, 0, TDBuffer, 17); //Read full device descriptor
ICTFBI 0:4edb816d21e1 97 rc = controlReceive(USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, GET_DESCRIPTOR, (USB_DESCRIPTOR_TYPE_DEVICE << 8)|(0), 0, m_controlDataBuf, 17);
ICTFBI 0:4edb816d21e1 98 if (rc)
ICTFBI 0:4edb816d21e1 99 {
ICTFBI 0:4edb816d21e1 100 //PRINT_Err(rc);
ICTFBI 0:4edb816d21e1 101 return (rc);
ICTFBI 0:4edb816d21e1 102 }
ICTFBI 0:4edb816d21e1 103 /*
ICTFBI 0:4edb816d21e1 104 rc = SerialCheckVidPid();
ICTFBI 0:4edb816d21e1 105 if (rc != OK) {
ICTFBI 0:4edb816d21e1 106 PRINT_Err(rc);
ICTFBI 0:4edb816d21e1 107 return (rc);
ICTFBI 0:4edb816d21e1 108 }
ICTFBI 0:4edb816d21e1 109 */
ICTFBI 0:4edb816d21e1 110 /**/
ICTFBI 0:4edb816d21e1 111
ICTFBI 0:4edb816d21e1 112 m_vid = *((uint16_t*)&m_controlDataBuf[8]);
ICTFBI 0:4edb816d21e1 113 m_pid = *((uint16_t*)&m_controlDataBuf[10]);
ICTFBI 0:4edb816d21e1 114
ICTFBI 0:4edb816d21e1 115 DBG("VID: %02x, PID: %02x\n", m_vid, m_pid);
ICTFBI 0:4edb816d21e1 116 /* Get the configuration descriptor */
ICTFBI 0:4edb816d21e1 117 //rc = HOST_GET_DESCRIPTOR(USB_DESCRIPTOR_TYPE_CONFIGURATION, 0, TDBuffer, 9);
ICTFBI 0:4edb816d21e1 118 rc = controlReceive(USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, GET_DESCRIPTOR, (USB_DESCRIPTOR_TYPE_CONFIGURATION << 8)|(0), 0, m_controlDataBuf, 9);
ICTFBI 0:4edb816d21e1 119 if (rc)
ICTFBI 0:4edb816d21e1 120 {
ICTFBI 0:4edb816d21e1 121 //PRINT_Err(rc);
ICTFBI 0:4edb816d21e1 122 return (rc);
ICTFBI 0:4edb816d21e1 123 }
ICTFBI 0:4edb816d21e1 124 /* Get the first configuration data */
ICTFBI 0:4edb816d21e1 125 //rc = HOST_GET_DESCRIPTOR(USB_DESCRIPTOR_TYPE_CONFIGURATION, 0, TDBuffer, *((uint16_t*)&TDBuffer[2]));
ICTFBI 0:4edb816d21e1 126 rc = controlReceive(USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, GET_DESCRIPTOR, (USB_DESCRIPTOR_TYPE_CONFIGURATION << 8)|(0), 0, m_controlDataBuf, *((uint16_t*)&m_controlDataBuf[2]));
ICTFBI 0:4edb816d21e1 127 if (rc)
ICTFBI 0:4edb816d21e1 128 {
ICTFBI 0:4edb816d21e1 129 //PRINT_Err(rc);
ICTFBI 0:4edb816d21e1 130 return (rc);
ICTFBI 0:4edb816d21e1 131 }
ICTFBI 0:4edb816d21e1 132
ICTFBI 0:4edb816d21e1 133 DBG("Desc len is %d\n", *((uint16_t*)&m_controlDataBuf[2]));
ICTFBI 0:4edb816d21e1 134
ICTFBI 0:4edb816d21e1 135 DBG("Set configuration\n");
ICTFBI 0:4edb816d21e1 136
ICTFBI 0:4edb816d21e1 137 //rc = USBH_SET_CONFIGURATION(1);/* Select device configuration 1 */
ICTFBI 0:4edb816d21e1 138 rc = controlSend(USB_HOST_TO_DEVICE | USB_RECIPIENT_DEVICE, SET_CONFIGURATION, 1, 0, NULL, 0);
ICTFBI 0:4edb816d21e1 139 if (rc)
ICTFBI 0:4edb816d21e1 140 {
ICTFBI 0:4edb816d21e1 141 // PRINT_Err(rc);
ICTFBI 0:4edb816d21e1 142 return rc;
ICTFBI 0:4edb816d21e1 143 }
ICTFBI 0:4edb816d21e1 144 wait_ms(100);/* Some devices may require this delay */
ICTFBI 0:4edb816d21e1 145
ICTFBI 0:4edb816d21e1 146 m_enumerated = true;
ICTFBI 0:4edb816d21e1 147 return USBERR_OK;
ICTFBI 0:4edb816d21e1 148 }
ICTFBI 0:4edb816d21e1 149
ICTFBI 0:4edb816d21e1 150 bool UsbDevice::connected()
ICTFBI 0:4edb816d21e1 151 {
ICTFBI 0:4edb816d21e1 152 return m_connected;
ICTFBI 0:4edb816d21e1 153 }
ICTFBI 0:4edb816d21e1 154
ICTFBI 0:4edb816d21e1 155 bool UsbDevice::enumerated()
ICTFBI 0:4edb816d21e1 156 {
ICTFBI 0:4edb816d21e1 157 return m_enumerated;
ICTFBI 0:4edb816d21e1 158 }
ICTFBI 0:4edb816d21e1 159
ICTFBI 0:4edb816d21e1 160 int UsbDevice::getPid()
ICTFBI 0:4edb816d21e1 161 {
ICTFBI 0:4edb816d21e1 162 return m_pid;
ICTFBI 0:4edb816d21e1 163 }
ICTFBI 0:4edb816d21e1 164
ICTFBI 0:4edb816d21e1 165 int UsbDevice::getVid()
ICTFBI 0:4edb816d21e1 166 {
ICTFBI 0:4edb816d21e1 167 return m_vid;
ICTFBI 0:4edb816d21e1 168 }
ICTFBI 0:4edb816d21e1 169
ICTFBI 0:4edb816d21e1 170 UsbErr UsbDevice::getConfigurationDescriptor(int config, uint8_t** pBuf)
ICTFBI 0:4edb816d21e1 171 {
ICTFBI 0:4edb816d21e1 172 //For now olny one config
ICTFBI 0:4edb816d21e1 173 *pBuf = m_controlDataBuf;
ICTFBI 0:4edb816d21e1 174 return USBERR_OK;
ICTFBI 0:4edb816d21e1 175 }
ICTFBI 0:4edb816d21e1 176
ICTFBI 0:4edb816d21e1 177 UsbErr UsbDevice::getInterfaceDescriptor(int config, int item, uint8_t** pBuf)
ICTFBI 0:4edb816d21e1 178 {
ICTFBI 0:4edb816d21e1 179 byte* desc_ptr = m_controlDataBuf;
ICTFBI 0:4edb816d21e1 180
ICTFBI 0:4edb816d21e1 181 /* if (desc_ptr[1] != USB_DESCRIPTOR_TYPE_CONFIGURATION)
ICTFBI 0:4edb816d21e1 182 {
ICTFBI 0:4edb816d21e1 183 return USBERR_BADCONFIG;
ICTFBI 0:4edb816d21e1 184 }*/
ICTFBI 0:4edb816d21e1 185
ICTFBI 0:4edb816d21e1 186 if(item>=m_controlDataBuf[4])//Interfaces count
ICTFBI 0:4edb816d21e1 187 return USBERR_NOTFOUND;
ICTFBI 0:4edb816d21e1 188
ICTFBI 0:4edb816d21e1 189 desc_ptr += desc_ptr[0];
ICTFBI 0:4edb816d21e1 190
ICTFBI 0:4edb816d21e1 191 *pBuf = NULL;
ICTFBI 0:4edb816d21e1 192
ICTFBI 0:4edb816d21e1 193 while (desc_ptr < m_controlDataBuf + *((uint16_t*)&m_controlDataBuf[2]))
ICTFBI 0:4edb816d21e1 194 {
ICTFBI 0:4edb816d21e1 195
ICTFBI 0:4edb816d21e1 196 switch (desc_ptr[1]) {
ICTFBI 0:4edb816d21e1 197 case USB_DESCRIPTOR_TYPE_INTERFACE:
ICTFBI 0:4edb816d21e1 198 if(desc_ptr[2] == item)
ICTFBI 0:4edb816d21e1 199 {
ICTFBI 0:4edb816d21e1 200 *pBuf = desc_ptr;
ICTFBI 0:4edb816d21e1 201 return USBERR_OK;
ICTFBI 0:4edb816d21e1 202 }
ICTFBI 0:4edb816d21e1 203 desc_ptr += desc_ptr[0]; // Move to next descriptor start
ICTFBI 0:4edb816d21e1 204 break;
ICTFBI 0:4edb816d21e1 205 }
ICTFBI 0:4edb816d21e1 206
ICTFBI 0:4edb816d21e1 207 }
ICTFBI 0:4edb816d21e1 208
ICTFBI 0:4edb816d21e1 209 if(*pBuf == NULL)
ICTFBI 0:4edb816d21e1 210 return USBERR_NOTFOUND;
ICTFBI 0:4edb816d21e1 211
ICTFBI 0:4edb816d21e1 212 return USBERR_OK;
ICTFBI 0:4edb816d21e1 213 }
ICTFBI 0:4edb816d21e1 214
ICTFBI 0:4edb816d21e1 215
ICTFBI 0:4edb816d21e1 216 UsbErr UsbDevice::setConfiguration(int config)
ICTFBI 0:4edb816d21e1 217 {
ICTFBI 0:4edb816d21e1 218 return USBERR_OK;
ICTFBI 0:4edb816d21e1 219 }
ICTFBI 0:4edb816d21e1 220
ICTFBI 0:4edb816d21e1 221 UsbErr UsbDevice::controlSend(byte requestType, byte request, word value, word index, const byte* buf, int len)
ICTFBI 0:4edb816d21e1 222 {
ICTFBI 0:4edb816d21e1 223 UsbErr rc;
ICTFBI 0:4edb816d21e1 224 fillControlBuf(requestType, request, value, index, len);
ICTFBI 0:4edb816d21e1 225 m_pControlEp->setNextToken(TD_SETUP);
ICTFBI 0:4edb816d21e1 226 rc = m_pControlEp->transfer(m_controlBuf, 8);
ICTFBI 0:4edb816d21e1 227 while(m_pControlEp->status() == USBERR_PROCESSING);
ICTFBI 0:4edb816d21e1 228 rc = (UsbErr) MIN(0, m_pControlEp->status());
ICTFBI 0:4edb816d21e1 229 if(rc)
ICTFBI 0:4edb816d21e1 230 return rc;
ICTFBI 0:4edb816d21e1 231 if(len)
ICTFBI 0:4edb816d21e1 232 {
ICTFBI 0:4edb816d21e1 233 m_pControlEp->setNextToken(TD_OUT);
ICTFBI 0:4edb816d21e1 234 rc = m_pControlEp->transfer((byte*)buf, len);
ICTFBI 0:4edb816d21e1 235 while(m_pControlEp->status() == USBERR_PROCESSING);
ICTFBI 0:4edb816d21e1 236 rc = (UsbErr) MIN(0, m_pControlEp->status());
ICTFBI 0:4edb816d21e1 237 if(rc)
ICTFBI 0:4edb816d21e1 238 return rc;
ICTFBI 0:4edb816d21e1 239 }
ICTFBI 0:4edb816d21e1 240 m_pControlEp->setNextToken(TD_IN);
ICTFBI 0:4edb816d21e1 241 rc = m_pControlEp->transfer(NULL, 0);
ICTFBI 0:4edb816d21e1 242 while(m_pControlEp->status() == USBERR_PROCESSING);
ICTFBI 0:4edb816d21e1 243 rc = (UsbErr) MIN(0, m_pControlEp->status());
ICTFBI 0:4edb816d21e1 244 if(rc)
ICTFBI 0:4edb816d21e1 245 return rc;
ICTFBI 0:4edb816d21e1 246 return USBERR_OK;
ICTFBI 0:4edb816d21e1 247 }
ICTFBI 0:4edb816d21e1 248
ICTFBI 0:4edb816d21e1 249 UsbErr UsbDevice::controlReceive(byte requestType, byte request, word value, word index, const byte* buf, int len)
ICTFBI 0:4edb816d21e1 250 {
ICTFBI 0:4edb816d21e1 251 UsbErr rc;
ICTFBI 0:4edb816d21e1 252 fillControlBuf(requestType, request, value, index, len);
ICTFBI 0:4edb816d21e1 253 m_pControlEp->setNextToken(TD_SETUP);
ICTFBI 0:4edb816d21e1 254 rc = m_pControlEp->transfer(m_controlBuf, 8);
ICTFBI 0:4edb816d21e1 255 while(m_pControlEp->status() == USBERR_PROCESSING);
ICTFBI 0:4edb816d21e1 256 rc = (UsbErr) MIN(0, m_pControlEp->status());
ICTFBI 0:4edb816d21e1 257 if(rc)
ICTFBI 0:4edb816d21e1 258 return rc;
ICTFBI 0:4edb816d21e1 259 if(len)
ICTFBI 0:4edb816d21e1 260 {
ICTFBI 0:4edb816d21e1 261 m_pControlEp->setNextToken(TD_IN);
ICTFBI 0:4edb816d21e1 262 rc = m_pControlEp->transfer( (byte*) buf, len);
ICTFBI 0:4edb816d21e1 263 while(m_pControlEp->status() == USBERR_PROCESSING);
ICTFBI 0:4edb816d21e1 264 rc = (UsbErr) MIN(0, m_pControlEp->status());
ICTFBI 0:4edb816d21e1 265 if(rc)
ICTFBI 0:4edb816d21e1 266 return rc;
ICTFBI 0:4edb816d21e1 267 }
ICTFBI 0:4edb816d21e1 268 m_pControlEp->setNextToken(TD_OUT);
ICTFBI 0:4edb816d21e1 269 rc = m_pControlEp->transfer(NULL, 0);
ICTFBI 0:4edb816d21e1 270 while(m_pControlEp->status() == USBERR_PROCESSING);
ICTFBI 0:4edb816d21e1 271 rc = (UsbErr) MIN(0, m_pControlEp->status());
ICTFBI 0:4edb816d21e1 272 if(rc)
ICTFBI 0:4edb816d21e1 273 return rc;
ICTFBI 0:4edb816d21e1 274 return USBERR_OK;
ICTFBI 0:4edb816d21e1 275 }
ICTFBI 0:4edb816d21e1 276
ICTFBI 0:4edb816d21e1 277 void UsbDevice::fillControlBuf(byte requestType, byte request, word value, word index, int len)
ICTFBI 0:4edb816d21e1 278 {
ICTFBI 0:4edb816d21e1 279 #ifdef __BIG_ENDIAN
ICTFBI 0:4edb816d21e1 280 #error "Must implement BE to LE conv here"
ICTFBI 0:4edb816d21e1 281 #endif
ICTFBI 0:4edb816d21e1 282 m_controlBuf[0] = requestType;
ICTFBI 0:4edb816d21e1 283 m_controlBuf[1] = request;
ICTFBI 0:4edb816d21e1 284 //We are in LE so it's fine
ICTFBI 0:4edb816d21e1 285 *((word*)&m_controlBuf[2]) = value;
ICTFBI 0:4edb816d21e1 286 *((word*)&m_controlBuf[4]) = index;
ICTFBI 0:4edb816d21e1 287 *((word*)&m_controlBuf[6]) = (word) len;
ICTFBI 0:4edb816d21e1 288 }
ICTFBI 0:4edb816d21e1 289
ICTFBI 0:4edb816d21e1 290
ICTFBI 0:4edb816d21e1 291 #endif