FloppyDiskDevice.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. /*
  2. * Copyright (c) 2019-2020, Jesse Buhagiar <jooster669@gmail.com>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include <Kernel/Devices/FloppyDiskDevice.h>
  27. #include <Kernel/VM/MemoryManager.h>
  28. #include <LibBareMetal/IO.h>
  29. namespace Kernel {
  30. // Uncomment me for a LOT of output
  31. //#define FLOPPY_DEBUG
  32. // THESE ARE OFFSETS!
  33. #define FLOPPY_STATUS_A 0x00 // ro
  34. #define FLOPPY_STATUS_B 0x01 // ro
  35. #define FLOPPY_DOR 0x02 // rw
  36. #define FLOPPY_TDR 0x03 // rw
  37. #define FLOPPY_MSR 0x04 // ro
  38. #define FLOPPY_DSR 0x04 // wo
  39. #define FLOPPY_FIFO 0x05
  40. #define FLOPPY_RSVD 0x06
  41. #define FLOPPY_DIR 0x07 // ro
  42. #define FLOPPY_CCR 0x07 // wo
  43. #define FLOPPY_STATUS_DIR 0x01
  44. #define FLOPPY_STATUS_WP 0x02
  45. #define FLOPPY_STATUS_INDX 0x04
  46. #define FLOPPY_STATUS_HDSEL 0x08
  47. #define FLOPPY_STATUS_TRK0 0x10
  48. #define FLOPPY_STATUS_STEP 0x20
  49. #define FLOPPY_STATUS_DRV2 0x40
  50. #define FLOPPY_STATUS_INTW 0x80 // A.K.A INT_PENDING
  51. #define FLOPPY_DOR_DRVSEL0 0x01
  52. #define FLOPPY_DOR_DRVSEL1 0x02
  53. #define FLOPPY_DOR_RESET 0x04
  54. #define FLOPPY_DOR_DMAGATE 0x08
  55. #define FLOPPY_DOR_MOTEN0 0x10
  56. #define FLOPPY_DOR_MOTEN1 0x20
  57. #define FLOPPY_DOR_MOTEN2 0x40
  58. #define FLOPPY_DOR_MOTEN3 0x80
  59. // Preset values to activate drive select and motor enable for each drive
  60. #define FLOPPY_DOR_DRV0 0x1C
  61. #define FLOPPY_DOR_DRV1 0x2D
  62. #define FLOPPY_DOR_DRV2 0x4E
  63. #define FLOPPY_DOR_DRV3 0x8F
  64. #define FLOPPY_MSR_FDD0BSY 0x01
  65. #define FLOPPY_MSR_FDD1BSY 0x02
  66. #define FLOPPY_MSR_FDD2BSY 0x04
  67. #define FLOPPY_MSR_FDD3BSY 0x08
  68. #define FLOPPY_MSR_FDCBSY 0x10
  69. #define FLOPPY_MSR_MODE 0x20 // 0 in DMA mode, 1 in PIO mode
  70. #define FLOPPY_MSR_DIO 0x40 // 0 FDC is expecting data from the CPU, 1 if FDC has data for CPU
  71. #define FLOPPY_MSR_RQM 0x80 // 0 Data register not ready, 1 data register ready
  72. #define FLOPPY_CCR_DRTESEL0 0x01
  73. #define FLOPPY_CCR_DRTESEL1 0x02
  74. #define FLOPPY_MT 0x80 // Multi-track selector. The controller treats 2 tracks (on side 0 and side 1) as a single track instead
  75. #define FLOPPY_MFM 0x40 // 1 Means this disk is double density (double sided??)
  76. #define FLOPPY_SK 0x20 // Skip flag. Skips sectors containing deleted data automatically for us :)
  77. #define SR0_OKAY (0x00) << 6
  78. #define SR0_ABORMAL_TERMINATION (0x01) << 6
  79. #define SR0_INVALID_CMD (0x02) << 6
  80. #define SR0_ABNORMAL_TERM_POLL (0x03) << 6
  81. #define FLOPPY_DMA_CHANNEL 2 // All FDCs are DMA channel 2
  82. #define IRQ_FLOPPY_DRIVE 6
  83. NonnullRefPtr<FloppyDiskDevice> FloppyDiskDevice::create(DriveType type)
  84. {
  85. return adopt(*new FloppyDiskDevice(type));
  86. }
  87. const char* FloppyDiskDevice::class_name() const
  88. {
  89. if (m_controller_version == 0x90)
  90. return "Intel 82078 Floppy Disk Controller";
  91. else if (m_controller_version == 0x80)
  92. return "NEC uPD765";
  93. return "Generic Floppy Disk Controller";
  94. }
  95. FloppyDiskDevice::FloppyDiskDevice(FloppyDiskDevice::DriveType type)
  96. : IRQHandler(IRQ_FLOPPY_DRIVE)
  97. , BlockDevice(89, (type == FloppyDiskDevice::DriveType::Master) ? 0 : 1, BYTES_PER_SECTOR)
  98. , m_io_base_addr((type == FloppyDiskDevice::DriveType::Master) ? 0x3F0 : 0x370)
  99. {
  100. initialize();
  101. }
  102. FloppyDiskDevice::~FloppyDiskDevice()
  103. {
  104. }
  105. bool FloppyDiskDevice::read_blocks(unsigned index, u16 count, u8* data)
  106. {
  107. return read_sectors_with_dma(index, count, data);
  108. }
  109. bool FloppyDiskDevice::write_blocks(unsigned index, u16 count, const u8* data)
  110. {
  111. return write_sectors_with_dma(index, count, data);
  112. ;
  113. }
  114. bool FloppyDiskDevice::read_sectors_with_dma(u16 lba, u16 count, u8* outbuf)
  115. {
  116. LOCKER(m_lock); // Acquire lock
  117. #ifdef FLOPPY_DEBUG
  118. klog() << "fdc: read_sectors_with_dma lba = " << lba << " count = " << count;
  119. #endif
  120. motor_enable(is_slave()); // Should I bother casting this?!
  121. write_ccr(0);
  122. recalibrate();
  123. if (!seek(lba)) {
  124. klog() << "fdc: failed to seek to lba = " << lba << "!";
  125. return false;
  126. }
  127. // We have to wait for about 300ms for the drive to spin up, because of
  128. // the inertia of the motor and diskette. This is only
  129. // important on real hardware
  130. // TODO: Fix this if you want to get it running on real hardware. This code doesn't allow
  131. // time for the disk to spin up.
  132. //u32 start = PIT::seconds_since_boot();
  133. //while(start < PIT::seconds_since_boot() + 1)
  134. // ;
  135. disable_irq();
  136. IO::out8(0xA, FLOPPY_DMA_CHANNEL | 0x4); // Channel 2 SEL, MASK_ON = 1
  137. IO::out8(0x0B, 0x56); // Begin DMA, Single Transfer, Increment, Auto, FDC -> RAM, Channel 2
  138. IO::out8(0xA, 0x2); // Unmask channel 2. The transfer will now begin
  139. // Translate the LBA address into something the FDC understands.
  140. u16 cylinder = lba2cylinder(lba);
  141. u16 head = lba2head(lba);
  142. u16 sector = lba2sector(lba);
  143. #ifdef FLOPPY_DEBUG
  144. klog() << "fdc: addr = 0x" << String::format("%x", lba * BYTES_PER_SECTOR) << " c = " << cylinder << " h = " << head << " s = " << sector;
  145. #endif
  146. // Intel recommends 3 attempts for a read/write
  147. for (int i = 0; i < 3; i++) {
  148. // Now actually send the command to the drive. This is a big one!
  149. send_byte(FLOPPY_MFM | FLOPPY_MT | FLOPPY_SK | static_cast<u8>(FloppyCommand::ReadData));
  150. send_byte((head << 2) | is_slave());
  151. send_byte(cylinder);
  152. send_byte(head);
  153. send_byte(sector);
  154. send_byte(SECTORS_PER_CYLINDER >> 8); // Yikes!
  155. send_byte(((sector + 1) >= SECTORS_PER_CYLINDER) ? SECTORS_PER_CYLINDER : sector + 1);
  156. send_byte(0x1b); // GPL3 value. The Datasheet doesn't really specify the values for this properly...
  157. send_byte(0xff);
  158. enable_irq();
  159. wait_for_irq(); // TODO: See if there was a lockup here via some "timeout counter"
  160. m_interrupted = false;
  161. // Flush FIFO
  162. // Let's check the value of Status Register 1 to ensure that
  163. // the command executed correctly
  164. u8 cmd_st0 = read_byte();
  165. if ((cmd_st0 & 0xc0) != 0) {
  166. klog() << "fdc: read failed with error code (st0) 0x" << String::format("%x", cmd_st0 >> 6);
  167. return false;
  168. }
  169. u8 cmd_st1 = read_byte();
  170. if (cmd_st1 != 0) {
  171. klog() << "fdc: read failed with error code (st1) 0x" << String::format("%x", cmd_st1);
  172. return false;
  173. }
  174. read_byte();
  175. u8 cyl = read_byte();
  176. read_byte();
  177. read_byte();
  178. read_byte();
  179. if (cyl != cylinder) {
  180. #ifdef FLOPPY_DEBUG
  181. klog() << "fdc: cyl != cylinder (cyl = " << cyl << " cylinder = " << cylinder << ")! Retrying...";
  182. #endif
  183. continue;
  184. }
  185. // Let the controller know we handled the interrupt
  186. send_byte(FloppyCommand::SenseInterrupt);
  187. u8 st0 = read_byte();
  188. u8 pcn = read_byte();
  189. static_cast<void>(st0);
  190. static_cast<void>(pcn);
  191. memcpy(outbuf, m_dma_buffer_page->paddr().as_ptr(), 512 * count);
  192. return true;
  193. }
  194. #ifdef FLOPPY_DEBUG
  195. klog() << "fdc: out of read attempts (check your hardware maybe!?)";
  196. #endif
  197. return false;
  198. }
  199. bool FloppyDiskDevice::write_sectors_with_dma(u16 lba, u16 count, const u8* inbuf)
  200. {
  201. LOCKER(m_lock); // Acquire lock
  202. #ifdef FLOPPY_DEBUG
  203. klog() << "fdc: write_sectors_with_dma lba = " << lba << " count = " << count;
  204. #endif
  205. motor_enable(is_slave() ? 1 : 0); // Should I bother casting this?!
  206. write_ccr(0);
  207. recalibrate(); // Recalibrate the drive
  208. if (!seek(lba)) {
  209. klog() << "fdc: failed to seek to lba = " << lba << "!";
  210. return false;
  211. }
  212. // We have to wait for about 300ms for the drive to spin up, because of
  213. // the inertia of the motor and diskette.
  214. // TODO: Fix this abomination please!
  215. //u32 start = PIT::seconds_since_boot();
  216. //while(start < PIT::seconds_since_boot() + 1)
  217. // ;
  218. disable_irq();
  219. IO::out8(0xA, FLOPPY_DMA_CHANNEL | 0x4); // Channel 2 SEL, MASK_ON = 1
  220. IO::out8(0x0B, 0x5A); // Begin DMA, Single Transfer, Increment, Auto, RAM -> FDC, Channel 2
  221. IO::out8(0xA, 0x2); // Unmask channel 2. The transfer will now begin
  222. u16 cylinder = lba2cylinder(lba);
  223. u16 head = lba2head(lba);
  224. u16 sector = lba2sector(lba);
  225. #ifdef FLOPPY_DEBUG
  226. klog() << "fdc: addr = 0x" << String::format("%x", lba * BYTES_PER_SECTOR) << " c = " << cylinder << " h = " << head << " s = " << sector;
  227. #endif
  228. for (int i = 0; i < 3; i++) {
  229. // Now actually send the command to the drive. This is a big one!
  230. send_byte(FLOPPY_MFM | FLOPPY_MT | static_cast<u8>(FloppyCommand::WriteData));
  231. send_byte(head << 2 | is_slave());
  232. send_byte(cylinder);
  233. send_byte(head);
  234. send_byte(sector);
  235. send_byte(SECTORS_PER_CYLINDER >> 8); // Yikes!
  236. send_byte((sector + 1) >= SECTORS_PER_CYLINDER ? SECTORS_PER_CYLINDER : sector + 1);
  237. send_byte(0x1b); // GPL3 value. The Datasheet doesn't really specify the values for this properly...
  238. send_byte(0xff);
  239. enable_irq();
  240. wait_for_irq(); // TODO: See if there was a lockup here via some "timeout counter"
  241. m_interrupted = false;
  242. // Flush FIFO
  243. u8 cmd_st0 = read_byte();
  244. if ((cmd_st0 & 0xc0) != 0) {
  245. klog() << "fdc: write failed! Error code 0x" << String::format("%x", cmd_st0 >> 6);
  246. return false;
  247. }
  248. u8 cmd_st1 = read_byte();
  249. if (cmd_st1 != 0) {
  250. klog() << "fdc: write failed with error code (st1) 0x" << String::format("%x", cmd_st1);
  251. return false;
  252. }
  253. read_byte();
  254. u8 cyl = read_byte();
  255. read_byte();
  256. read_byte();
  257. read_byte();
  258. if (cyl != cylinder) {
  259. #ifdef FLOPPY_DEBUG
  260. klog() << "fdc: cyl != cylinder (cyl = " << cyl << " cylinder = " << cylinder << ")! Retrying...";
  261. #endif
  262. continue;
  263. }
  264. // Let the controller know we handled the interrupt
  265. send_byte(FloppyCommand::SenseInterrupt);
  266. u8 st0 = read_byte();
  267. u8 pcn = read_byte();
  268. static_cast<void>(st0);
  269. static_cast<void>(pcn);
  270. memcpy(m_dma_buffer_page->paddr().as_ptr(), inbuf, 512 * count);
  271. return true;
  272. }
  273. #ifdef FLOPPY_DEBUG
  274. klog() << "fdc: out of read attempts (check your hardware maybe!?)";
  275. #endif
  276. return false;
  277. }
  278. bool FloppyDiskDevice::wait_for_irq()
  279. {
  280. #ifdef FLOPPY_DEBUG
  281. klog() << "fdc: Waiting for interrupt...";
  282. #endif
  283. while (!m_interrupted) {
  284. Scheduler::yield();
  285. }
  286. memory_barrier();
  287. return true;
  288. }
  289. void FloppyDiskDevice::handle_irq(RegisterState&)
  290. {
  291. // The only thing we need to do is acknowledge the IRQ happened
  292. m_interrupted = true;
  293. #ifdef FLOPPY_DEBUG
  294. klog() << "fdc: Received IRQ!";
  295. #endif
  296. }
  297. void FloppyDiskDevice::send_byte(u8 value) const
  298. {
  299. for (int i = 0; i < 1024; i++) {
  300. if (read_msr() & FLOPPY_MSR_RQM) {
  301. IO::out8(m_io_base_addr + FLOPPY_FIFO, value);
  302. return;
  303. }
  304. }
  305. #ifdef FLOPPY_DEBUG
  306. klog() << "fdc: FIFO write timed out!";
  307. #endif
  308. }
  309. void FloppyDiskDevice::send_byte(FloppyCommand value) const
  310. {
  311. for (int i = 0; i < 1024; i++) {
  312. if (read_msr() & FLOPPY_MSR_RQM) {
  313. IO::out8(m_io_base_addr + FLOPPY_FIFO, static_cast<u8>(value));
  314. return;
  315. }
  316. }
  317. #ifdef FLOPPY_DEBUG
  318. klog() << "fdc: FIFO write timed out!";
  319. #endif
  320. }
  321. u8 FloppyDiskDevice::read_byte() const
  322. {
  323. for (int i = 0; i < 1024; i++) {
  324. if (read_msr() & (FLOPPY_MSR_RQM | FLOPPY_MSR_DIO)) {
  325. return IO::in8(m_io_base_addr + FLOPPY_FIFO);
  326. }
  327. }
  328. #ifdef FLOPPY_DEBUG
  329. klog() << "fdc: FIFO read timed out!";
  330. #endif
  331. return 0xff;
  332. }
  333. void FloppyDiskDevice::write_dor(u8 value) const
  334. {
  335. IO::out8(m_io_base_addr + FLOPPY_DOR, value);
  336. }
  337. void FloppyDiskDevice::write_ccr(u8 value) const
  338. {
  339. IO::out8(m_io_base_addr + FLOPPY_CCR, value);
  340. }
  341. u8 FloppyDiskDevice::read_msr() const
  342. {
  343. return IO::in8(m_io_base_addr + FLOPPY_MSR);
  344. }
  345. void FloppyDiskDevice::motor_enable(bool slave) const
  346. {
  347. u8 val = slave ? 0x2D : 0x1C;
  348. write_dor(val);
  349. }
  350. bool FloppyDiskDevice::is_busy() const
  351. {
  352. return read_msr() & FLOPPY_MSR;
  353. }
  354. bool FloppyDiskDevice::recalibrate()
  355. {
  356. #ifdef FLOPPY_DEBUG
  357. klog() << "fdc: recalibrating drive...";
  358. #endif
  359. u8 slave = is_slave();
  360. motor_enable(slave);
  361. for (int i = 0; i < 16; i++) {
  362. send_byte(FloppyCommand::Recalibrate);
  363. send_byte(slave);
  364. wait_for_irq();
  365. m_interrupted = false;
  366. send_byte(FloppyCommand::SenseInterrupt);
  367. u8 st0 = read_byte();
  368. u8 pcn = read_byte();
  369. static_cast<void>(st0);
  370. if (pcn == 0)
  371. return true;
  372. }
  373. #ifdef FLOPPY_DEBUG
  374. klog() << "fdc: failed to calibrate drive (check your hardware!)";
  375. #endif
  376. return false;
  377. }
  378. bool FloppyDiskDevice::seek(u16 lba)
  379. {
  380. u8 head = lba2head(lba) & 0x01;
  381. u8 cylinder = lba2cylinder(lba) & 0xff;
  382. u8 slave = is_slave();
  383. // First, we need to enable the correct drive motor
  384. motor_enable(slave);
  385. #ifdef FLOPPY_DEBUG
  386. klog() << "fdc: seeking to cylinder " << cylinder << " on side " << head << " on drive " << slave;
  387. #endif
  388. // Try at most 5 times to seek to the desired cylinder
  389. for (int attempt = 0; attempt < 5; attempt++) {
  390. send_byte(FloppyCommand::Seek);
  391. send_byte((head << 2) | slave);
  392. send_byte(cylinder);
  393. wait_for_irq();
  394. m_interrupted = false;
  395. send_byte(FloppyCommand::SenseInterrupt);
  396. u8 st0 = read_byte();
  397. u8 pcn = read_byte();
  398. if ((st0 >> 5) != 1 || pcn != cylinder || (st0 & 0x01)) {
  399. #ifdef FLOPPY_DEBUG
  400. klog() << "fdc: failed to seek to cylinder " << cylinder << " on attempt " << attempt << "!";
  401. #endif
  402. continue;
  403. }
  404. return true;
  405. }
  406. klog() << "fdc: failed to seek after 3 attempts! Aborting...";
  407. return false;
  408. }
  409. // This is following Intel's datasheet for the 82077, page 41
  410. void FloppyDiskDevice::initialize()
  411. {
  412. #ifdef FLOPPY_DEBUG
  413. klog() << "fdc: m_io_base = 0x" << String::format("%x", m_io_base_addr) << " IRQn = " << IRQ_FLOPPY_DRIVE;
  414. #endif
  415. enable_irq();
  416. // Get the version of the Floppy Disk Controller
  417. send_byte(FloppyCommand::Version);
  418. m_controller_version = read_byte();
  419. klog() << "fdc: Version = 0x" << String::format("%x", m_controller_version);
  420. // Reset
  421. write_dor(0);
  422. write_dor(FLOPPY_DOR_RESET | FLOPPY_DOR_DMAGATE);
  423. write_ccr(0);
  424. wait_for_irq();
  425. m_interrupted = false;
  426. // "If (and only if) drive polling mode is turned on, send 4 Sense Interrupt commands (required). "
  427. // Sorry OSDev, but the Intel Manual states otherwise. This ALWAYS needs to be performed.
  428. for (int i = 0; i < 4; i++) {
  429. send_byte(FloppyCommand::SenseInterrupt);
  430. u8 sr0 = read_byte();
  431. u8 trk = read_byte();
  432. klog() << "sr0 = 0x" << String::format("%x", sr0) << ", cyl = 0x" << String::format("%x", trk);
  433. }
  434. // This is hardcoded for a 3.5" floppy disk drive
  435. send_byte(FloppyCommand::Specify);
  436. send_byte(0x08); // (SRT << 4) | HUT
  437. send_byte(0x0A); // (HLT << 1) | NDMA
  438. // Allocate a buffer page for us to read into. This only needs to be one sector in size.
  439. m_dma_buffer_page = MM.allocate_supervisor_physical_page();
  440. #ifdef FLOPPY_DEBUG
  441. klog() << "fdc: allocated supervisor page at paddr 0x", String::format("%x", m_dma_buffer_page->paddr());
  442. #endif
  443. // Now, let's initialise channel 2 of the DMA controller!
  444. // This only needs to be done here, then we can just change the direction of
  445. // the transfer
  446. IO::out8(0xA, FLOPPY_DMA_CHANNEL | 0x4); // Channel 2 SEL, MASK_ON = 1
  447. IO::out8(0xC, 0xFF); // Reset Master Flip Flop
  448. // Set the buffer page address (the lower 16-bits)
  449. IO::out8(0x4, m_dma_buffer_page->paddr().get() & 0xff);
  450. IO::out8(0x4, (m_dma_buffer_page->paddr().get() >> 8) & 0xff);
  451. IO::out8(0xC, 0xFF); // Reset Master Flip Flop again
  452. IO::out8(0x05, (SECTORS_PER_CYLINDER * BYTES_PER_SECTOR) & 0xff);
  453. IO::out8(0x05, (SECTORS_PER_CYLINDER * BYTES_PER_SECTOR) >> 8);
  454. IO::out8(0x81, (m_dma_buffer_page->paddr().get() >> 16) & 0xff); // Supervisor page could be a 24-bit address, so set the External Page R/W register
  455. IO::out8(0xA, 0x2); // Unmask Channel 2
  456. #ifdef FLOPPY_DEBUG
  457. klog() << "fdc: fd" << (is_slave() ? 1 : 0) << " initialised succesfully!";
  458. #endif
  459. }
  460. }