Added to LPC4088-USBHost the USBHostMidi class. Plugin an usb midi interface to the usb host of lpc4088 allows to send midi event to the midi interface. For the moment I can not be able to get event from the interface by using the attacheNoteOn or other triggers...

Dependencies:   FATFileSystem mbed-rtos

Fork of LPC4088-USBHost by Norimasa Okamoto

Committer:
Grag38
Date:
Mon Apr 06 12:46:58 2015 +0000
Revision:
1:d652de69bd1a
Parent:
0:148fca6fd246
Added USBHostMidi to drive midi interface.; ; Tested to send Midi messages from LPC4088. This works.; ; Need to test with incomming events.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
va009039 0:148fca6fd246 1 /* mbed USBHost Library
va009039 0:148fca6fd246 2 * Copyright (c) 2006-2013 ARM Limited
va009039 0:148fca6fd246 3 *
va009039 0:148fca6fd246 4 * Licensed under the Apache License, Version 2.0 (the "License");
va009039 0:148fca6fd246 5 * you may not use this file except in compliance with the License.
va009039 0:148fca6fd246 6 * You may obtain a copy of the License at
va009039 0:148fca6fd246 7 *
va009039 0:148fca6fd246 8 * http://www.apache.org/licenses/LICENSE-2.0
va009039 0:148fca6fd246 9 *
va009039 0:148fca6fd246 10 * Unless required by applicable law or agreed to in writing, software
va009039 0:148fca6fd246 11 * distributed under the License is distributed on an "AS IS" BASIS,
va009039 0:148fca6fd246 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
va009039 0:148fca6fd246 13 * See the License for the specific language governing permissions and
va009039 0:148fca6fd246 14 * limitations under the License.
va009039 0:148fca6fd246 15 */
va009039 0:148fca6fd246 16
va009039 0:148fca6fd246 17 #include "USBHost.h"
va009039 0:148fca6fd246 18 #define USB_DEBUG
va009039 0:148fca6fd246 19 #include "BaseUsbHostDebug.h"
va009039 0:148fca6fd246 20 #define TEST
va009039 0:148fca6fd246 21 #include "BaseUsbHostTest.h"
va009039 0:148fca6fd246 22
va009039 0:148fca6fd246 23 USBHost* USBHost::inst = NULL;
va009039 0:148fca6fd246 24
va009039 0:148fca6fd246 25 USBHost* USBHost::getHostInst() {
va009039 0:148fca6fd246 26 if (inst == NULL) {
va009039 0:148fca6fd246 27 inst = new USBHost();
va009039 0:148fca6fd246 28 inst->init();
va009039 0:148fca6fd246 29 }
va009039 0:148fca6fd246 30 return inst;
va009039 0:148fca6fd246 31 }
va009039 0:148fca6fd246 32
va009039 0:148fca6fd246 33 void USBHost::poll()
va009039 0:148fca6fd246 34 {
va009039 0:148fca6fd246 35 if (inst) {
va009039 0:148fca6fd246 36 inst->task();
va009039 0:148fca6fd246 37 }
va009039 0:148fca6fd246 38 }
va009039 0:148fca6fd246 39
va009039 0:148fca6fd246 40 USBHost::USBHost() {
va009039 0:148fca6fd246 41 }
va009039 0:148fca6fd246 42
va009039 0:148fca6fd246 43 /* virtual */ bool USBHost::addDevice(USBDeviceConnected* parent, int port, bool lowSpeed) {
va009039 0:148fca6fd246 44 USBDeviceConnected* dev = new USBDeviceConnected;
va009039 0:148fca6fd246 45 USBEndpoint* ep = new USBEndpoint(dev);
va009039 0:148fca6fd246 46 dev->init(0, port, lowSpeed);
va009039 0:148fca6fd246 47 dev->setAddress(0);
va009039 0:148fca6fd246 48 dev->setEpCtl(ep);
va009039 0:148fca6fd246 49 uint8_t desc[18];
va009039 0:148fca6fd246 50 wait_ms(100);
va009039 0:148fca6fd246 51
va009039 0:148fca6fd246 52 int rc = controlRead(dev, 0x80, GET_DESCRIPTOR, 1<<8, 0, desc, 8);
va009039 0:148fca6fd246 53 USB_TEST_ASSERT(rc == USB_TYPE_OK);
va009039 0:148fca6fd246 54 if (rc != USB_TYPE_OK) {
va009039 0:148fca6fd246 55 USB_ERR("ADD DEVICE FAILD");
va009039 0:148fca6fd246 56 }
va009039 0:148fca6fd246 57 USB_DBG_HEX(desc, 8);
va009039 0:148fca6fd246 58 DeviceDescriptor* dev_desc = reinterpret_cast<DeviceDescriptor*>(desc);
va009039 0:148fca6fd246 59 ep->setSize(dev_desc->bMaxPacketSize);
va009039 0:148fca6fd246 60
va009039 0:148fca6fd246 61 int new_addr = USBDeviceConnected::getNewAddress();
va009039 0:148fca6fd246 62 rc = controlWrite(dev, 0x00, SET_ADDRESS, new_addr, 0, NULL, 0);
va009039 0:148fca6fd246 63 USB_TEST_ASSERT(rc == USB_TYPE_OK);
va009039 0:148fca6fd246 64 dev->setAddress(new_addr);
va009039 0:148fca6fd246 65 wait_ms(100);
va009039 0:148fca6fd246 66
va009039 0:148fca6fd246 67 rc = controlRead(dev, 0x80, GET_DESCRIPTOR, 1<<8, 0, desc, sizeof(desc));
va009039 0:148fca6fd246 68 USB_TEST_ASSERT(rc == USB_TYPE_OK);
va009039 0:148fca6fd246 69 USB_DBG_HEX(desc, sizeof(desc));
va009039 0:148fca6fd246 70
va009039 0:148fca6fd246 71 dev->setVid(dev_desc->idVendor);
va009039 0:148fca6fd246 72 dev->setPid(dev_desc->idProduct);
va009039 0:148fca6fd246 73 dev->setClass(dev_desc->bDeviceClass);
va009039 0:148fca6fd246 74 USB_INFO("parent:%p port:%d speed:%s VID:%04x PID:%04x class:%02x addr:%d",
va009039 0:148fca6fd246 75 parent, port, (lowSpeed ? "low " : "full"), dev->getVid(), dev->getPid(), dev->getClass(),
va009039 0:148fca6fd246 76 dev->getAddress());
va009039 0:148fca6fd246 77
va009039 0:148fca6fd246 78 DeviceLists.push_back(dev);
va009039 0:148fca6fd246 79
va009039 0:148fca6fd246 80 if (dev->getClass() == HUB_CLASS) {
va009039 0:148fca6fd246 81 const int config = 1;
va009039 0:148fca6fd246 82 int rc = controlWrite(dev, 0x00, SET_CONFIGURATION, config, 0, NULL, 0);
va009039 0:148fca6fd246 83 USB_TEST_ASSERT(rc == USB_TYPE_OK);
va009039 0:148fca6fd246 84 wait_ms(100);
va009039 0:148fca6fd246 85 Hub(dev);
va009039 0:148fca6fd246 86 }
va009039 0:148fca6fd246 87 return true;
va009039 0:148fca6fd246 88 }
va009039 0:148fca6fd246 89
va009039 0:148fca6fd246 90 // enumerate a device with the control USBEndpoint
va009039 0:148fca6fd246 91 USB_TYPE USBHost::enumerate(USBDeviceConnected * dev, IUSBEnumerator* pEnumerator)
va009039 0:148fca6fd246 92 {
va009039 0:148fca6fd246 93 if (dev->getClass() == HUB_CLASS) { // skip hub class
va009039 0:148fca6fd246 94 return USB_TYPE_OK;
va009039 0:148fca6fd246 95 }
va009039 0:148fca6fd246 96 uint8_t desc[18];
va009039 0:148fca6fd246 97 USB_TYPE rc = controlRead(dev, 0x80, GET_DESCRIPTOR, 1<<8, 0, desc, sizeof(desc));
va009039 0:148fca6fd246 98 USB_TEST_ASSERT(rc == USB_TYPE_OK);
va009039 0:148fca6fd246 99 USB_DBG_HEX(desc, sizeof(desc));
va009039 0:148fca6fd246 100 if (rc != USB_TYPE_OK) {
va009039 0:148fca6fd246 101 return rc;
va009039 0:148fca6fd246 102 }
va009039 0:148fca6fd246 103 DeviceDescriptor* dev_desc = reinterpret_cast<DeviceDescriptor*>(desc);
va009039 0:148fca6fd246 104 dev->setClass(dev_desc->bDeviceClass);
va009039 0:148fca6fd246 105 pEnumerator->setVidPid(dev->getVid(), dev->getPid());
va009039 0:148fca6fd246 106
va009039 0:148fca6fd246 107 rc = controlRead(dev, 0x80, GET_DESCRIPTOR, 2<<8, 0, desc, 4);
va009039 0:148fca6fd246 108 USB_TEST_ASSERT(rc == USB_TYPE_OK);
va009039 0:148fca6fd246 109 USB_DBG_HEX(desc, 4);
va009039 0:148fca6fd246 110
va009039 0:148fca6fd246 111 int TotalLength = desc[2]|desc[3]<<8;
va009039 0:148fca6fd246 112 uint8_t* buf = new uint8_t[TotalLength];
va009039 0:148fca6fd246 113 rc = controlRead(dev, 0x80, GET_DESCRIPTOR, 2<<8, 0, buf, TotalLength);
va009039 0:148fca6fd246 114 USB_TEST_ASSERT(rc == USB_TYPE_OK);
va009039 0:148fca6fd246 115 //USB_DBG_HEX(buf, TotalLength);
va009039 0:148fca6fd246 116
va009039 0:148fca6fd246 117 // Parse the configuration descriptor
va009039 0:148fca6fd246 118 parseConfDescr(dev, buf, TotalLength, pEnumerator);
va009039 0:148fca6fd246 119 delete[] buf;
va009039 0:148fca6fd246 120 // only set configuration if not enumerated before
va009039 0:148fca6fd246 121 if (!dev->isEnumerated()) {
va009039 0:148fca6fd246 122 USB_DBG("Set configuration 1 on dev: %p", dev);
va009039 0:148fca6fd246 123 // sixth step: set configuration (only 1 supported)
va009039 0:148fca6fd246 124 int config = 1;
va009039 0:148fca6fd246 125 USB_TYPE res = controlWrite(dev, 0x00, SET_CONFIGURATION, config, 0, NULL, 0);
va009039 0:148fca6fd246 126 if (res != USB_TYPE_OK) {
va009039 0:148fca6fd246 127 USB_ERR("SET CONF FAILED");
va009039 0:148fca6fd246 128 return res;
va009039 0:148fca6fd246 129 }
va009039 0:148fca6fd246 130 // Some devices may require this delay
va009039 0:148fca6fd246 131 wait_ms(100);
va009039 0:148fca6fd246 132 dev->setEnumerated();
va009039 0:148fca6fd246 133 // Now the device is enumerated!
va009039 0:148fca6fd246 134 USB_DBG("dev %p is enumerated", dev);
va009039 0:148fca6fd246 135 }
va009039 0:148fca6fd246 136 return USB_TYPE_OK;
va009039 0:148fca6fd246 137 }
va009039 0:148fca6fd246 138
va009039 0:148fca6fd246 139 // this method fills the USBDeviceConnected object: class,.... . It also add endpoints found in the descriptor.
va009039 0:148fca6fd246 140 void USBHost::parseConfDescr(USBDeviceConnected * dev, uint8_t * conf_descr, uint32_t len, IUSBEnumerator* pEnumerator)
va009039 0:148fca6fd246 141 {
va009039 0:148fca6fd246 142 uint32_t index = 0;
va009039 0:148fca6fd246 143 uint32_t len_desc = 0;
va009039 0:148fca6fd246 144 uint8_t id = 0;
va009039 0:148fca6fd246 145 USBEndpoint * ep = NULL;
va009039 0:148fca6fd246 146 uint8_t intf_nb = 0;
va009039 0:148fca6fd246 147 bool parsing_intf = false;
va009039 0:148fca6fd246 148 uint8_t current_intf = 0;
va009039 0:148fca6fd246 149 EndpointDescriptor* ep_desc;
va009039 0:148fca6fd246 150
va009039 0:148fca6fd246 151 while (index < len) {
va009039 0:148fca6fd246 152 len_desc = conf_descr[index];
va009039 0:148fca6fd246 153 id = conf_descr[index+1];
va009039 0:148fca6fd246 154 USB_DBG_HEX(conf_descr+index, len_desc);
va009039 0:148fca6fd246 155 switch (id) {
va009039 0:148fca6fd246 156 case CONFIGURATION_DESCRIPTOR:
va009039 0:148fca6fd246 157 USB_DBG("dev: %p has %d intf", dev, conf_descr[4]);
va009039 0:148fca6fd246 158 dev->setNbIntf(conf_descr[4]);
va009039 0:148fca6fd246 159 break;
va009039 0:148fca6fd246 160 case INTERFACE_DESCRIPTOR:
va009039 0:148fca6fd246 161 if(pEnumerator->parseInterface(conf_descr[index + 2], conf_descr[index + 5], conf_descr[index + 6], conf_descr[index + 7])) {
va009039 0:148fca6fd246 162 intf_nb++;
va009039 0:148fca6fd246 163 current_intf = conf_descr[index + 2];
va009039 0:148fca6fd246 164 dev->addInterface(current_intf, conf_descr[index + 5], conf_descr[index + 6], conf_descr[index + 7]);
va009039 0:148fca6fd246 165 USB_DBG("ADD INTF %d on device %p: class: %d, subclass: %d, proto: %d", current_intf, dev, conf_descr[index + 5],conf_descr[index + 6],conf_descr[index + 7]);
va009039 0:148fca6fd246 166 parsing_intf = true;
va009039 0:148fca6fd246 167 } else {
va009039 0:148fca6fd246 168 parsing_intf = false;
va009039 0:148fca6fd246 169 }
va009039 0:148fca6fd246 170 break;
va009039 0:148fca6fd246 171 case ENDPOINT_DESCRIPTOR:
va009039 0:148fca6fd246 172 ep_desc = reinterpret_cast<EndpointDescriptor*>(conf_descr+index);
va009039 0:148fca6fd246 173 if (parsing_intf && (intf_nb <= MAX_INTF) ) {
va009039 0:148fca6fd246 174 ENDPOINT_TYPE type = (ENDPOINT_TYPE)(ep_desc->bmAttributes & 0x03);
va009039 0:148fca6fd246 175 ENDPOINT_DIRECTION dir = (ep_desc->bEndpointAddress & 0x80) ? IN : OUT;
va009039 0:148fca6fd246 176 if(pEnumerator->useEndpoint(current_intf, type, dir)) {
va009039 0:148fca6fd246 177 ep = new USBEndpoint(dev);
va009039 0:148fca6fd246 178 ep->init(type, dir, ep_desc->wMaxPacketSize, ep_desc->bEndpointAddress);
va009039 0:148fca6fd246 179 USB_DBG("ADD USBEndpoint %p, on interf %d on device %p", ep, current_intf, dev);
va009039 0:148fca6fd246 180 dev->addEndpoint(current_intf, ep);
va009039 0:148fca6fd246 181 }
va009039 0:148fca6fd246 182 }
va009039 0:148fca6fd246 183 break;
va009039 0:148fca6fd246 184 case HID_DESCRIPTOR:
va009039 0:148fca6fd246 185 //lenReportDescr = conf_descr[index + 7] | (conf_descr[index + 8] << 8);
va009039 0:148fca6fd246 186 break;
va009039 0:148fca6fd246 187 default:
va009039 0:148fca6fd246 188 break;
va009039 0:148fca6fd246 189 }
va009039 0:148fca6fd246 190 index += len_desc;
va009039 0:148fca6fd246 191 }
va009039 0:148fca6fd246 192 }
va009039 0:148fca6fd246 193
va009039 0:148fca6fd246 194 USB_TYPE USBHost::controlRead(USBDeviceConnected* dev, uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, uint8_t * buf, uint32_t len) {
va009039 0:148fca6fd246 195 USBEndpoint* ep = dev->getEpCtl();
va009039 0:148fca6fd246 196 return controlRead(ep, requestType, request, value, index, buf, len);
va009039 0:148fca6fd246 197 }
va009039 0:148fca6fd246 198
va009039 0:148fca6fd246 199 USB_TYPE USBHost::controlWrite(USBDeviceConnected* dev, uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, uint8_t * buf, uint32_t len) {
va009039 0:148fca6fd246 200 USBEndpoint* ep = dev->getEpCtl();
va009039 0:148fca6fd246 201 return controlWrite(ep, requestType, request, value, index, buf, len);
va009039 0:148fca6fd246 202 }
va009039 0:148fca6fd246 203
va009039 0:148fca6fd246 204 USB_TYPE USBHost::controlRead(USBEndpoint* ep, uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, uint8_t* buf, uint32_t len)
va009039 0:148fca6fd246 205 {
va009039 0:148fca6fd246 206 SETUP_PACKET setup(requestType, request, value, index, len);
va009039 0:148fca6fd246 207 int result = token_setup(ep, &setup, len); // setup stage
va009039 0:148fca6fd246 208 if (result < 0) {
va009039 0:148fca6fd246 209 return USB_TYPE_ERROR;
va009039 0:148fca6fd246 210 }
va009039 0:148fca6fd246 211
va009039 0:148fca6fd246 212 result = token_in(ep, buf, len); // data stage
va009039 0:148fca6fd246 213 if (result < 0) {
va009039 0:148fca6fd246 214 return USB_TYPE_ERROR;
va009039 0:148fca6fd246 215 }
va009039 0:148fca6fd246 216 int read_len = result;
va009039 0:148fca6fd246 217
va009039 0:148fca6fd246 218 ep->m_pED->setToggleDATA1();
va009039 0:148fca6fd246 219 result = token_out(ep); // status stage
va009039 0:148fca6fd246 220 if (result < 0) {
va009039 0:148fca6fd246 221 return USB_TYPE_ERROR;
va009039 0:148fca6fd246 222 }
va009039 0:148fca6fd246 223 ep->setLengthTransferred(read_len);
va009039 0:148fca6fd246 224 return USB_TYPE_OK;
va009039 0:148fca6fd246 225 }
va009039 0:148fca6fd246 226
va009039 0:148fca6fd246 227 USB_TYPE USBHost::controlWrite(USBEndpoint* ep, uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, uint8_t * buf, uint32_t len)
va009039 0:148fca6fd246 228 {
va009039 0:148fca6fd246 229 SETUP_PACKET setup(requestType, request, value, index, len);
va009039 0:148fca6fd246 230 int result = token_setup(ep, &setup, len); // setup stage
va009039 0:148fca6fd246 231 if (result < 0) {
va009039 0:148fca6fd246 232 return USB_TYPE_ERROR;
va009039 0:148fca6fd246 233 }
va009039 0:148fca6fd246 234 int write_len = 0;
va009039 0:148fca6fd246 235 if (buf != NULL) {
va009039 0:148fca6fd246 236 result = token_out(ep, buf, len); // data stage
va009039 0:148fca6fd246 237 if (result < 0) {
va009039 0:148fca6fd246 238 return USB_TYPE_ERROR;
va009039 0:148fca6fd246 239 }
va009039 0:148fca6fd246 240 write_len = result;
va009039 0:148fca6fd246 241 }
va009039 0:148fca6fd246 242 ep->m_pED->setToggleDATA1();
va009039 0:148fca6fd246 243 result = token_in(ep); // status stage
va009039 0:148fca6fd246 244 if (result < 0) {
va009039 0:148fca6fd246 245 return USB_TYPE_ERROR;
va009039 0:148fca6fd246 246 }
va009039 0:148fca6fd246 247 ep->setLengthTransferred(write_len);
va009039 0:148fca6fd246 248 return USB_TYPE_OK;
va009039 0:148fca6fd246 249 }
va009039 0:148fca6fd246 250
va009039 0:148fca6fd246 251 USB_TYPE USBHost::bulkRead(USBDeviceConnected* dev, USBEndpoint* ep, uint8_t* buf, uint32_t len, bool blocking) {
va009039 0:148fca6fd246 252 if (!blocking) {
va009039 0:148fca6fd246 253 ep->setBuffer(buf, len);
va009039 0:148fca6fd246 254 ep_queue.push(ep);
va009039 0:148fca6fd246 255 token_inNB(ep, buf, len);
va009039 0:148fca6fd246 256 return USB_TYPE_PROCESSING;
va009039 0:148fca6fd246 257 }
va009039 0:148fca6fd246 258 int result = token_in(ep, buf, len);
va009039 0:148fca6fd246 259 if (result >= 0) {
va009039 0:148fca6fd246 260 ep->setLengthTransferred(result);
va009039 0:148fca6fd246 261 return USB_TYPE_OK;
va009039 0:148fca6fd246 262 }
va009039 0:148fca6fd246 263 return USB_TYPE_ERROR;
va009039 0:148fca6fd246 264 }
va009039 0:148fca6fd246 265
va009039 0:148fca6fd246 266 USB_TYPE USBHost::bulkWrite(USBDeviceConnected * dev, USBEndpoint* ep, uint8_t * buf, uint32_t len, bool blocking)
va009039 0:148fca6fd246 267 {
va009039 0:148fca6fd246 268 int result = token_out(ep, buf, len);
va009039 0:148fca6fd246 269 if (result >= 0) {
va009039 0:148fca6fd246 270 ep->setLengthTransferred(result);
va009039 0:148fca6fd246 271 return USB_TYPE_OK;
va009039 0:148fca6fd246 272 }
va009039 0:148fca6fd246 273 return USB_TYPE_ERROR;
va009039 0:148fca6fd246 274 }
va009039 0:148fca6fd246 275
va009039 0:148fca6fd246 276 USB_TYPE USBHost::interruptRead(USBDeviceConnected * dev, USBEndpoint* ep, uint8_t* buf, uint32_t len, bool blocking) {
va009039 0:148fca6fd246 277 if (!blocking) {
va009039 0:148fca6fd246 278 ep->setBuffer(buf, len);
va009039 0:148fca6fd246 279 ep_queue.push(ep);
va009039 0:148fca6fd246 280 token_inNB(ep, buf, len);
va009039 0:148fca6fd246 281 return USB_TYPE_PROCESSING;
va009039 0:148fca6fd246 282 }
va009039 0:148fca6fd246 283 int result = token_in(ep, buf, len);
va009039 0:148fca6fd246 284 if (result >= 0) {
va009039 0:148fca6fd246 285 ep->setLengthTransferred(result);
va009039 0:148fca6fd246 286 return USB_TYPE_OK;
va009039 0:148fca6fd246 287 }
va009039 0:148fca6fd246 288 return USB_TYPE_ERROR;
va009039 0:148fca6fd246 289 }
va009039 0:148fca6fd246 290
va009039 0:148fca6fd246 291 int USBHost::interruptReadNB(USBEndpoint* ep, uint8_t* data, int size) {
va009039 0:148fca6fd246 292 if (ep->getState() != USB_TYPE_PROCESSING) {
va009039 0:148fca6fd246 293 ep->setState(USB_TYPE_PROCESSING);
va009039 0:148fca6fd246 294 ep->setBuffer(data, size);
va009039 0:148fca6fd246 295 token_inNB(ep, data, size);
va009039 0:148fca6fd246 296 }
va009039 0:148fca6fd246 297 if (token_inNB_result(ep) != USB_TYPE_PROCESSING) {
va009039 0:148fca6fd246 298 return ep->getLengthTransferred();
va009039 0:148fca6fd246 299 }
va009039 0:148fca6fd246 300 return -1;
va009039 0:148fca6fd246 301 }
va009039 0:148fca6fd246 302
va009039 0:148fca6fd246 303 int USBHost::bulkReadNB(USBEndpoint*ep, uint8_t* data, int size) {
va009039 0:148fca6fd246 304 return interruptReadNB(ep, data, size);
va009039 0:148fca6fd246 305 }
va009039 0:148fca6fd246 306
va009039 0:148fca6fd246 307 void USBHost::task() {
va009039 0:148fca6fd246 308 USBEndpoint* ep = ep_queue.pop();
va009039 0:148fca6fd246 309 if (ep) {
va009039 0:148fca6fd246 310 if (token_inNB_result(ep) == USB_TYPE_OK) {
va009039 0:148fca6fd246 311 ep->call();
va009039 0:148fca6fd246 312 } else {
va009039 0:148fca6fd246 313 ep_queue.push(ep);
va009039 0:148fca6fd246 314 }
va009039 0:148fca6fd246 315 }
va009039 0:148fca6fd246 316 }
va009039 0:148fca6fd246 317