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