Skip to content

Commit 8d9bc66

Browse files
authored
Merge pull request #115 from mihaip/upstream-multiple
Fixes for READ_MULTIPLE and WRITE_MULTIPLE commands
2 parents ebf4b0e + 52f4d84 commit 8d9bc66

File tree

3 files changed

+28
-11
lines changed

3 files changed

+28
-11
lines changed

devices/common/ata/atabasedevice.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,11 @@ uint16_t AtaBaseDevice::read(const uint8_t reg_addr) {
7070
this->chunk_cnt -= 2;
7171
if (this->chunk_cnt <= 0) {
7272
this->xfer_cnt -= this->chunk_size;
73-
if (this->xfer_cnt <= 0)
73+
if (this->xfer_cnt <= 0) {
74+
this->xfer_cnt = 0;
7475
this->r_status &= ~DRQ;
75-
else {
76-
this->chunk_cnt = this->chunk_size;
76+
} else {
77+
this->chunk_cnt = std::min(this->xfer_cnt, this->chunk_size);
7778
this->update_intrq(1);
7879
}
7980
}
@@ -114,12 +115,13 @@ void AtaBaseDevice::write(const uint8_t reg_addr, const uint16_t value) {
114115
this->post_xfer_action();
115116
this->xfer_cnt -= this->chunk_size;
116117
if (this->xfer_cnt <= 0) { // transfer complete?
118+
this->xfer_cnt = 0;
117119
this->r_status &= ~DRQ;
118120
this->r_status &= ~BSY;
119121
this->update_intrq(1);
120122
} else {
121123
this->cur_data_ptr = this->data_ptr;
122-
this->chunk_cnt = this->chunk_size;
124+
this->chunk_cnt = std::min(this->xfer_cnt, this->chunk_size);
123125
this->signal_data_ready();
124126
}
125127
}

devices/common/ata/atahd.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ int AtaHardDisk::perform_command() {
106106
uint32_t ints_size = ATA_HD_SEC_SIZE;
107107
if (this->r_command == READ_MULTIPLE) {
108108
if (this->sec_per_block == 0) {
109-
LOG_F(ERROR, "%s: READ MULTIPLE with SET MULTIPLE==0", this->name.c_str());
109+
LOG_F(ERROR, "%s: cannot do a READ_MULTIPLE with unset sec_per_block", this->name.c_str());
110110
this->r_status |= ERR;
111111
this->r_status &= ~BSY;
112112
break;
@@ -131,7 +131,7 @@ int AtaHardDisk::perform_command() {
131131
uint32_t ints_size = ATA_HD_SEC_SIZE;
132132
if (this->r_command == WRITE_MULTIPLE) {
133133
if (this->sec_per_block == 0) {
134-
LOG_F(ERROR, "%s: WRITE MULTIPLE with SET MULTIPLE==0", this->name.c_str());
134+
LOG_F(ERROR, "%s: cannot do a WRITE_MULTIPLE with unset sec_per_block", this->name.c_str());
135135
this->r_status |= ERR;
136136
this->r_status &= ~BSY;
137137
break;
@@ -140,8 +140,9 @@ int AtaHardDisk::perform_command() {
140140
}
141141
this->prepare_xfer(xfer_size, ints_size);
142142
this->post_xfer_action = [this]() {
143-
this->hdd_img.write(this->data_ptr, this->cur_fpos, this->chunk_size);
144-
this->cur_fpos += this->chunk_size;
143+
uint64_t write_len = (this->cur_data_ptr - this->data_ptr) * sizeof(this->data_ptr[0]);
144+
this->hdd_img.write(this->data_ptr, this->cur_fpos, write_len);
145+
this->cur_fpos += write_len;
145146
};
146147
this->r_status |= DRQ;
147148
this->r_status &= ~BSY;
@@ -168,10 +169,12 @@ int AtaHardDisk::perform_command() {
168169
case SET_MULTIPLE_MODE: // this command is mandatory for ATA devices
169170
if (!this->r_sect_count || this->r_sect_count > 128 ||
170171
std::bitset<8>(this->r_sect_count).count() != 1) { // power of two?
172+
LOG_F(ERROR, "%s: SET_MULTIPLE_MODE not suported, invalid r_sect_count (%d)", this->name.c_str(), this->r_sect_count);
171173
this->multiple_enabled = false;
172174
this->r_error |= ABRT;
173175
this->r_status |= ERR;
174176
} else {
177+
LOG_F(INFO, "%s: SET_MULTIPLE_MODE, r_sect_count=%d", this->name.c_str(), this->r_sect_count);
175178
this->sec_per_block = this->r_sect_count;
176179
this->multiple_enabled = true;
177180
}
@@ -235,9 +238,21 @@ void AtaHardDisk::prepare_identify_info() {
235238
std::memset(this->data_buf, 0, sizeof(this->data_buf));
236239

237240
buf_ptr[ 0] = 0x0040; // ATA device, non-removable media, non-removable drive
238-
buf_ptr[47] = this->sec_per_block; // block size of READ_MULTIPLE/WRITE_MULTIPLE
239241
buf_ptr[49] = 0x0200; // report LBA support
240242

243+
// Maximum number of logical sectors per data block that the device supports
244+
// for READ_MULTIPLE/WRITE_MULTIPLE commands.
245+
const int max_sec_per_block = 8;
246+
if (max_sec_per_block > 1) {
247+
buf_ptr[47] = 0x8000 | max_sec_per_block;
248+
}
249+
// If bit 8 of word 59 is set to one, then bits 7:0 indicate the number of
250+
// logical sectors that shall be transferred per data block for
251+
// READ_MULTIPLE/WRITE_MULTIPLE commands.
252+
if (this->sec_per_block) {
253+
buf_ptr[59] = 0x100 | this->sec_per_block;
254+
}
255+
241256
buf_ptr[ 1] = this->cylinders;
242257
buf_ptr[ 3] = this->heads;
243258
buf_ptr[ 6] = this->sectors;

devices/common/ata/atahd.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ class AtaHardDisk : public AtaBaseDevice
6969
uint8_t heads;
7070
uint8_t sectors;
7171

72-
uint8_t sec_per_block = 8; // sectors per block for READ_MULTIPLE/WRITE_MULTIPLE
73-
bool multiple_enabled = true; // READ_MULTIPLE/WRITE_MULTIPLE enabled
72+
uint8_t sec_per_block = 0; // sectors per block for READ_MULTIPLE/WRITE_MULTIPLE
73+
bool multiple_enabled = false; // READ_MULTIPLE/WRITE_MULTIPLE enabled
7474

7575
char * buffer = new char[1 <<17];
7676
};

0 commit comments

Comments
 (0)