ide_ctrl.cc revision 11264:dc389d2d2f79
1/* 2 * Copyright (c) 2013 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Copyright (c) 2004-2005 The Regents of The University of Michigan 15 * All rights reserved. 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions are 19 * met: redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer; 21 * redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution; 24 * neither the name of the copyright holders nor the names of its 25 * contributors may be used to endorse or promote products derived from 26 * this software without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 * 40 * Authors: Andrew Schultz 41 * Ali Saidi 42 * Miguel Serrano 43 */ 44 45#include "dev/storage/ide_ctrl.hh" 46 47#include <string> 48 49#include "cpu/intr_control.hh" 50#include "debug/IdeCtrl.hh" 51#include "dev/storage/ide_disk.hh" 52#include "mem/packet.hh" 53#include "mem/packet_access.hh" 54#include "params/IdeController.hh" 55#include "sim/byteswap.hh" 56 57// clang complains about std::set being overloaded with Packet::set if 58// we open up the entire namespace std 59using std::string; 60 61// Bus master IDE registers 62enum BMIRegOffset { 63 BMICommand = 0x0, 64 BMIStatus = 0x2, 65 BMIDescTablePtr = 0x4 66}; 67 68// PCI config space registers 69enum ConfRegOffset { 70 PrimaryTiming = 0x40, 71 SecondaryTiming = 0x42, 72 DeviceTiming = 0x44, 73 UDMAControl = 0x48, 74 UDMATiming = 0x4A, 75 IDEConfig = 0x54 76}; 77 78static const uint16_t timeRegWithDecodeEn = 0x8000; 79 80IdeController::Channel::Channel( 81 string newName, Addr _cmdSize, Addr _ctrlSize) : 82 _name(newName), 83 cmdAddr(0), cmdSize(_cmdSize), ctrlAddr(0), ctrlSize(_ctrlSize), 84 master(NULL), slave(NULL), selected(NULL) 85{ 86 memset(&bmiRegs, 0, sizeof(bmiRegs)); 87 bmiRegs.status.dmaCap0 = 1; 88 bmiRegs.status.dmaCap1 = 1; 89} 90 91IdeController::Channel::~Channel() 92{ 93} 94 95IdeController::IdeController(Params *p) 96 : PciDevice(p), primary(name() + ".primary", BARSize[0], BARSize[1]), 97 secondary(name() + ".secondary", BARSize[2], BARSize[3]), 98 bmiAddr(0), bmiSize(BARSize[4]), 99 primaryTiming(htole(timeRegWithDecodeEn)), 100 secondaryTiming(htole(timeRegWithDecodeEn)), 101 deviceTiming(0), udmaControl(0), udmaTiming(0), ideConfig(0), 102 ioEnabled(false), bmEnabled(false), 103 ioShift(p->io_shift), ctrlOffset(p->ctrl_offset) 104{ 105 if (params()->disks.size() > 3) 106 panic("IDE controllers support a maximum of 4 devices attached!\n"); 107 108 // Assign the disks to channels 109 int numDisks = params()->disks.size(); 110 if (numDisks > 0) 111 primary.master = params()->disks[0]; 112 if (numDisks > 1) 113 primary.slave = params()->disks[1]; 114 if (numDisks > 2) 115 secondary.master = params()->disks[2]; 116 if (numDisks > 3) 117 secondary.slave = params()->disks[3]; 118 119 for (int i = 0; i < params()->disks.size(); i++) { 120 params()->disks[i]->setController(this); 121 } 122 primary.select(false); 123 secondary.select(false); 124 125 if ((BARAddrs[0] & ~BAR_IO_MASK) && (!legacyIO[0] || ioShift)) { 126 primary.cmdAddr = BARAddrs[0]; primary.cmdSize = BARSize[0]; 127 primary.ctrlAddr = BARAddrs[1]; primary.ctrlSize = BARSize[1]; 128 } 129 if ((BARAddrs[2] & ~BAR_IO_MASK) && (!legacyIO[2] || ioShift)) { 130 secondary.cmdAddr = BARAddrs[2]; secondary.cmdSize = BARSize[2]; 131 secondary.ctrlAddr = BARAddrs[3]; secondary.ctrlSize = BARSize[3]; 132 } 133 134 ioEnabled = (config.command & htole(PCI_CMD_IOSE)); 135 bmEnabled = (config.command & htole(PCI_CMD_BME)); 136} 137 138bool 139IdeController::isDiskSelected(IdeDisk *diskPtr) 140{ 141 return (primary.selected == diskPtr || secondary.selected == diskPtr); 142} 143 144void 145IdeController::intrPost() 146{ 147 primary.bmiRegs.status.intStatus = 1; 148 PciDevice::intrPost(); 149} 150 151void 152IdeController::setDmaComplete(IdeDisk *disk) 153{ 154 Channel *channel; 155 if (disk == primary.master || disk == primary.slave) { 156 channel = &primary; 157 } else if (disk == secondary.master || disk == secondary.slave) { 158 channel = &secondary; 159 } else { 160 panic("Unable to find disk based on pointer %#x\n", disk); 161 } 162 163 channel->bmiRegs.command.startStop = 0; 164 channel->bmiRegs.status.active = 0; 165 channel->bmiRegs.status.intStatus = 1; 166} 167 168Tick 169IdeController::readConfig(PacketPtr pkt) 170{ 171 int offset = pkt->getAddr() & PCI_CONFIG_SIZE; 172 if (offset < PCI_DEVICE_SPECIFIC) { 173 return PciDevice::readConfig(pkt); 174 } 175 176 switch (pkt->getSize()) { 177 case sizeof(uint8_t): 178 switch (offset) { 179 case DeviceTiming: 180 pkt->set<uint8_t>(deviceTiming); 181 break; 182 case UDMAControl: 183 pkt->set<uint8_t>(udmaControl); 184 break; 185 case PrimaryTiming + 1: 186 pkt->set<uint8_t>(bits(htole(primaryTiming), 15, 8)); 187 break; 188 case SecondaryTiming + 1: 189 pkt->set<uint8_t>(bits(htole(secondaryTiming), 15, 8)); 190 break; 191 case IDEConfig: 192 pkt->set<uint8_t>(bits(htole(ideConfig), 7, 0)); 193 break; 194 case IDEConfig + 1: 195 pkt->set<uint8_t>(bits(htole(ideConfig), 15, 8)); 196 break; 197 default: 198 panic("Invalid PCI configuration read for size 1 at offset: %#x!\n", 199 offset); 200 } 201 DPRINTF(IdeCtrl, "PCI read offset: %#x size: 1 data: %#x\n", offset, 202 (uint32_t)pkt->get<uint8_t>()); 203 break; 204 case sizeof(uint16_t): 205 switch (offset) { 206 case UDMAControl: 207 pkt->set<uint16_t>(udmaControl); 208 break; 209 case PrimaryTiming: 210 pkt->set<uint16_t>(primaryTiming); 211 break; 212 case SecondaryTiming: 213 pkt->set<uint16_t>(secondaryTiming); 214 break; 215 case UDMATiming: 216 pkt->set<uint16_t>(udmaTiming); 217 break; 218 case IDEConfig: 219 pkt->set<uint16_t>(ideConfig); 220 break; 221 default: 222 panic("Invalid PCI configuration read for size 2 offset: %#x!\n", 223 offset); 224 } 225 DPRINTF(IdeCtrl, "PCI read offset: %#x size: 2 data: %#x\n", offset, 226 (uint32_t)pkt->get<uint16_t>()); 227 break; 228 case sizeof(uint32_t): 229 switch (offset) { 230 case PrimaryTiming: 231 pkt->set<uint32_t>(primaryTiming); 232 break; 233 case IDEConfig: 234 pkt->set<uint32_t>(ideConfig); 235 break; 236 default: 237 panic("No 32bit reads implemented for this device."); 238 } 239 DPRINTF(IdeCtrl, "PCI read offset: %#x size: 4 data: %#x\n", offset, 240 (uint32_t)pkt->get<uint32_t>()); 241 break; 242 default: 243 panic("invalid access size(?) for PCI configspace!\n"); 244 } 245 pkt->makeAtomicResponse(); 246 return configDelay; 247} 248 249 250Tick 251IdeController::writeConfig(PacketPtr pkt) 252{ 253 int offset = pkt->getAddr() & PCI_CONFIG_SIZE; 254 if (offset < PCI_DEVICE_SPECIFIC) { 255 PciDevice::writeConfig(pkt); 256 } else { 257 switch (pkt->getSize()) { 258 case sizeof(uint8_t): 259 switch (offset) { 260 case DeviceTiming: 261 deviceTiming = pkt->get<uint8_t>(); 262 break; 263 case UDMAControl: 264 udmaControl = pkt->get<uint8_t>(); 265 break; 266 case IDEConfig: 267 replaceBits(ideConfig, 7, 0, pkt->get<uint8_t>()); 268 break; 269 case IDEConfig + 1: 270 replaceBits(ideConfig, 15, 8, pkt->get<uint8_t>()); 271 break; 272 default: 273 panic("Invalid PCI configuration write " 274 "for size 1 offset: %#x!\n", offset); 275 } 276 DPRINTF(IdeCtrl, "PCI write offset: %#x size: 1 data: %#x\n", 277 offset, (uint32_t)pkt->get<uint8_t>()); 278 break; 279 case sizeof(uint16_t): 280 switch (offset) { 281 case UDMAControl: 282 udmaControl = pkt->get<uint16_t>(); 283 break; 284 case PrimaryTiming: 285 primaryTiming = pkt->get<uint16_t>(); 286 break; 287 case SecondaryTiming: 288 secondaryTiming = pkt->get<uint16_t>(); 289 break; 290 case UDMATiming: 291 udmaTiming = pkt->get<uint16_t>(); 292 break; 293 case IDEConfig: 294 ideConfig = pkt->get<uint16_t>(); 295 break; 296 default: 297 panic("Invalid PCI configuration write " 298 "for size 2 offset: %#x!\n", 299 offset); 300 } 301 DPRINTF(IdeCtrl, "PCI write offset: %#x size: 2 data: %#x\n", 302 offset, (uint32_t)pkt->get<uint16_t>()); 303 break; 304 case sizeof(uint32_t): 305 switch (offset) { 306 case PrimaryTiming: 307 primaryTiming = pkt->get<uint32_t>(); 308 break; 309 case IDEConfig: 310 ideConfig = pkt->get<uint32_t>(); 311 break; 312 default: 313 panic("Write of unimplemented PCI config. register: %x\n", offset); 314 } 315 break; 316 default: 317 panic("invalid access size(?) for PCI configspace!\n"); 318 } 319 pkt->makeAtomicResponse(); 320 } 321 322 /* Trap command register writes and enable IO/BM as appropriate as well as 323 * BARs. */ 324 switch(offset) { 325 case PCI0_BASE_ADDR0: 326 if (BARAddrs[0] != 0) 327 primary.cmdAddr = BARAddrs[0]; 328 break; 329 330 case PCI0_BASE_ADDR1: 331 if (BARAddrs[1] != 0) 332 primary.ctrlAddr = BARAddrs[1]; 333 break; 334 335 case PCI0_BASE_ADDR2: 336 if (BARAddrs[2] != 0) 337 secondary.cmdAddr = BARAddrs[2]; 338 break; 339 340 case PCI0_BASE_ADDR3: 341 if (BARAddrs[3] != 0) 342 secondary.ctrlAddr = BARAddrs[3]; 343 break; 344 345 case PCI0_BASE_ADDR4: 346 if (BARAddrs[4] != 0) 347 bmiAddr = BARAddrs[4]; 348 break; 349 350 case PCI_COMMAND: 351 DPRINTF(IdeCtrl, "Writing to PCI Command val: %#x\n", config.command); 352 ioEnabled = (config.command & htole(PCI_CMD_IOSE)); 353 bmEnabled = (config.command & htole(PCI_CMD_BME)); 354 break; 355 } 356 return configDelay; 357} 358 359void 360IdeController::Channel::accessCommand(Addr offset, 361 int size, uint8_t *data, bool read) 362{ 363 const Addr SelectOffset = 6; 364 const uint8_t SelectDevBit = 0x10; 365 366 if (!read && offset == SelectOffset) 367 select(*data & SelectDevBit); 368 369 if (selected == NULL) { 370 assert(size == sizeof(uint8_t)); 371 *data = 0; 372 } else if (read) { 373 selected->readCommand(offset, size, data); 374 } else { 375 selected->writeCommand(offset, size, data); 376 } 377} 378 379void 380IdeController::Channel::accessControl(Addr offset, 381 int size, uint8_t *data, bool read) 382{ 383 if (selected == NULL) { 384 assert(size == sizeof(uint8_t)); 385 *data = 0; 386 } else if (read) { 387 selected->readControl(offset, size, data); 388 } else { 389 selected->writeControl(offset, size, data); 390 } 391} 392 393void 394IdeController::Channel::accessBMI(Addr offset, 395 int size, uint8_t *data, bool read) 396{ 397 assert(offset + size <= sizeof(BMIRegs)); 398 if (read) { 399 memcpy(data, (uint8_t *)&bmiRegs + offset, size); 400 } else { 401 switch (offset) { 402 case BMICommand: 403 { 404 if (size != sizeof(uint8_t)) 405 panic("Invalid BMIC write size: %x\n", size); 406 407 BMICommandReg oldVal = bmiRegs.command; 408 BMICommandReg newVal = *data; 409 410 // if a DMA transfer is in progress, R/W control cannot change 411 if (oldVal.startStop && oldVal.rw != newVal.rw) 412 oldVal.rw = newVal.rw; 413 414 if (oldVal.startStop != newVal.startStop) { 415 if (selected == NULL) 416 panic("DMA start for disk which does not exist\n"); 417 418 if (oldVal.startStop) { 419 DPRINTF(IdeCtrl, "Stopping DMA transfer\n"); 420 bmiRegs.status.active = 0; 421 422 selected->abortDma(); 423 } else { 424 DPRINTF(IdeCtrl, "Starting DMA transfer\n"); 425 bmiRegs.status.active = 1; 426 427 selected->startDma(letoh(bmiRegs.bmidtp)); 428 } 429 } 430 431 bmiRegs.command = newVal; 432 } 433 break; 434 case BMIStatus: 435 { 436 if (size != sizeof(uint8_t)) 437 panic("Invalid BMIS write size: %x\n", size); 438 439 BMIStatusReg oldVal = bmiRegs.status; 440 BMIStatusReg newVal = *data; 441 442 // the BMIDEA bit is read only 443 newVal.active = oldVal.active; 444 445 // to reset (set 0) IDEINTS and IDEDMAE, write 1 to each 446 if ((oldVal.intStatus == 1) && (newVal.intStatus == 1)) { 447 newVal.intStatus = 0; // clear the interrupt? 448 } else { 449 // Assigning two bitunion fields to each other does not 450 // work as intended, so we need to use this temporary variable 451 // to get around the bug. 452 uint8_t tmp = oldVal.intStatus; 453 newVal.intStatus = tmp; 454 } 455 if ((oldVal.dmaError == 1) && (newVal.dmaError == 1)) { 456 newVal.dmaError = 0; 457 } else { 458 uint8_t tmp = oldVal.dmaError; 459 newVal.dmaError = tmp; 460 } 461 462 bmiRegs.status = newVal; 463 } 464 break; 465 case BMIDescTablePtr: 466 if (size != sizeof(uint32_t)) 467 panic("Invalid BMIDTP write size: %x\n", size); 468 bmiRegs.bmidtp = htole(*(uint32_t *)data & ~0x3); 469 break; 470 default: 471 if (size != sizeof(uint8_t) && size != sizeof(uint16_t) && 472 size != sizeof(uint32_t)) 473 panic("IDE controller write of invalid write size: %x\n", size); 474 memcpy((uint8_t *)&bmiRegs + offset, data, size); 475 } 476 } 477} 478 479void 480IdeController::dispatchAccess(PacketPtr pkt, bool read) 481{ 482 if (pkt->getSize() != 1 && pkt->getSize() != 2 && pkt->getSize() !=4) 483 panic("Bad IDE read size: %d\n", pkt->getSize()); 484 485 if (!ioEnabled) { 486 pkt->makeAtomicResponse(); 487 DPRINTF(IdeCtrl, "io not enabled\n"); 488 return; 489 } 490 491 Addr addr = pkt->getAddr(); 492 int size = pkt->getSize(); 493 uint8_t *dataPtr = pkt->getPtr<uint8_t>(); 494 495 if (addr >= primary.cmdAddr && 496 addr < (primary.cmdAddr + primary.cmdSize)) { 497 addr -= primary.cmdAddr; 498 // linux may have shifted the address by ioShift, 499 // here we shift it back, similarly for ctrlOffset. 500 addr >>= ioShift; 501 primary.accessCommand(addr, size, dataPtr, read); 502 } else if (addr >= primary.ctrlAddr && 503 addr < (primary.ctrlAddr + primary.ctrlSize)) { 504 addr -= primary.ctrlAddr; 505 addr += ctrlOffset; 506 primary.accessControl(addr, size, dataPtr, read); 507 } else if (addr >= secondary.cmdAddr && 508 addr < (secondary.cmdAddr + secondary.cmdSize)) { 509 addr -= secondary.cmdAddr; 510 secondary.accessCommand(addr, size, dataPtr, read); 511 } else if (addr >= secondary.ctrlAddr && 512 addr < (secondary.ctrlAddr + secondary.ctrlSize)) { 513 addr -= secondary.ctrlAddr; 514 secondary.accessControl(addr, size, dataPtr, read); 515 } else if (addr >= bmiAddr && addr < (bmiAddr + bmiSize)) { 516 if (!read && !bmEnabled) 517 return; 518 addr -= bmiAddr; 519 if (addr < sizeof(Channel::BMIRegs)) { 520 primary.accessBMI(addr, size, dataPtr, read); 521 } else { 522 addr -= sizeof(Channel::BMIRegs); 523 secondary.accessBMI(addr, size, dataPtr, read); 524 } 525 } else { 526 panic("IDE controller access to invalid address: %#x\n", addr); 527 } 528 529#ifndef NDEBUG 530 uint32_t data; 531 if (pkt->getSize() == 1) 532 data = pkt->get<uint8_t>(); 533 else if (pkt->getSize() == 2) 534 data = pkt->get<uint16_t>(); 535 else 536 data = pkt->get<uint32_t>(); 537 DPRINTF(IdeCtrl, "%s from offset: %#x size: %#x data: %#x\n", 538 read ? "Read" : "Write", pkt->getAddr(), pkt->getSize(), data); 539#endif 540 541 pkt->makeAtomicResponse(); 542} 543 544Tick 545IdeController::read(PacketPtr pkt) 546{ 547 dispatchAccess(pkt, true); 548 return pioDelay; 549} 550 551Tick 552IdeController::write(PacketPtr pkt) 553{ 554 dispatchAccess(pkt, false); 555 return pioDelay; 556} 557 558void 559IdeController::serialize(CheckpointOut &cp) const 560{ 561 // Serialize the PciDevice base class 562 PciDevice::serialize(cp); 563 564 // Serialize channels 565 primary.serialize("primary", cp); 566 secondary.serialize("secondary", cp); 567 568 // Serialize config registers 569 SERIALIZE_SCALAR(primaryTiming); 570 SERIALIZE_SCALAR(secondaryTiming); 571 SERIALIZE_SCALAR(deviceTiming); 572 SERIALIZE_SCALAR(udmaControl); 573 SERIALIZE_SCALAR(udmaTiming); 574 SERIALIZE_SCALAR(ideConfig); 575 576 // Serialize internal state 577 SERIALIZE_SCALAR(ioEnabled); 578 SERIALIZE_SCALAR(bmEnabled); 579 SERIALIZE_SCALAR(bmiAddr); 580 SERIALIZE_SCALAR(bmiSize); 581} 582 583void 584IdeController::Channel::serialize(const std::string &base, 585 CheckpointOut &cp) const 586{ 587 paramOut(cp, base + ".cmdAddr", cmdAddr); 588 paramOut(cp, base + ".cmdSize", cmdSize); 589 paramOut(cp, base + ".ctrlAddr", ctrlAddr); 590 paramOut(cp, base + ".ctrlSize", ctrlSize); 591 uint8_t command = bmiRegs.command; 592 paramOut(cp, base + ".bmiRegs.command", command); 593 paramOut(cp, base + ".bmiRegs.reserved0", bmiRegs.reserved0); 594 uint8_t status = bmiRegs.status; 595 paramOut(cp, base + ".bmiRegs.status", status); 596 paramOut(cp, base + ".bmiRegs.reserved1", bmiRegs.reserved1); 597 paramOut(cp, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp); 598 paramOut(cp, base + ".selectBit", selectBit); 599} 600 601void 602IdeController::unserialize(CheckpointIn &cp) 603{ 604 // Unserialize the PciDevice base class 605 PciDevice::unserialize(cp); 606 607 // Unserialize channels 608 primary.unserialize("primary", cp); 609 secondary.unserialize("secondary", cp); 610 611 // Unserialize config registers 612 UNSERIALIZE_SCALAR(primaryTiming); 613 UNSERIALIZE_SCALAR(secondaryTiming); 614 UNSERIALIZE_SCALAR(deviceTiming); 615 UNSERIALIZE_SCALAR(udmaControl); 616 UNSERIALIZE_SCALAR(udmaTiming); 617 UNSERIALIZE_SCALAR(ideConfig); 618 619 // Unserialize internal state 620 UNSERIALIZE_SCALAR(ioEnabled); 621 UNSERIALIZE_SCALAR(bmEnabled); 622 UNSERIALIZE_SCALAR(bmiAddr); 623 UNSERIALIZE_SCALAR(bmiSize); 624} 625 626void 627IdeController::Channel::unserialize(const std::string &base, CheckpointIn &cp) 628{ 629 paramIn(cp, base + ".cmdAddr", cmdAddr); 630 paramIn(cp, base + ".cmdSize", cmdSize); 631 paramIn(cp, base + ".ctrlAddr", ctrlAddr); 632 paramIn(cp, base + ".ctrlSize", ctrlSize); 633 uint8_t command; 634 paramIn(cp, base +".bmiRegs.command", command); 635 bmiRegs.command = command; 636 paramIn(cp, base + ".bmiRegs.reserved0", bmiRegs.reserved0); 637 uint8_t status; 638 paramIn(cp, base + ".bmiRegs.status", status); 639 bmiRegs.status = status; 640 paramIn(cp, base + ".bmiRegs.reserved1", bmiRegs.reserved1); 641 paramIn(cp, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp); 642 paramIn(cp, base + ".selectBit", selectBit); 643 select(selectBit); 644} 645 646IdeController * 647IdeControllerParams::create() 648{ 649 return new IdeController(this); 650} 651