Some work for asynchronus USB packet handling (needed by disk/cdrom seek emulation).
- Common USB code prepared for async USB packet handling (ported from an earlier Qemu version). - Added simple async packet handling to OHCI (partly ported from earlier Qemu). - Implemented USB cdrom seek emulation for hubs with async packet support. The legacy code is still present for hubs without this feature. - For UHCI and xHCI the callback is set to NULL to force the usage of the legacy cdrom read code. TODO: clean up OHCI code, async support for UHCI and xHCI, seek emulation for USB harddisk and floppy.
This commit is contained in:
parent
8db5305b18
commit
491dc311ca
@ -91,6 +91,7 @@ scsi_device_t::scsi_device_t(device_image_t *_hdimage, int _tcq,
|
||||
locked = 0;
|
||||
inserted = 1;
|
||||
max_lba = (hdimage->hd_size / 512) - 1;
|
||||
curr_lba = max_lba;
|
||||
sprintf(drive_serial_str, "%d", serial_number++);
|
||||
seek_timer_index =
|
||||
DEV_register_timer(this, seek_timer_handler, 1000, 0, 0, "USB HD seek");
|
||||
@ -113,6 +114,7 @@ scsi_device_t::scsi_device_t(cdrom_base_c *_cdrom, int _tcq,
|
||||
locked = 0;
|
||||
inserted = 0;
|
||||
max_lba = 0;
|
||||
curr_lba = 0;
|
||||
sprintf(drive_serial_str, "%d", serial_number++);
|
||||
seek_timer_index =
|
||||
DEV_register_timer(this, seek_timer_handler, 1000, 0, 0, "USB CD seek");
|
||||
@ -152,6 +154,7 @@ void scsi_device_t::register_state(bx_list_c *parent, const char *name)
|
||||
bx_list_c *list = new bx_list_c(parent, name, "");
|
||||
new bx_shadow_num_c(list, "sense", &sense);
|
||||
new bx_shadow_bool_c(list, "locked", &locked);
|
||||
new bx_shadow_num_c(list, "curr_lba", &curr_lba);
|
||||
bx_param_bool_c *requests = new bx_param_bool_c(list, "requests", NULL, NULL, 0);
|
||||
requests->set_sr_handlers(this, scsireq_save_handler, scsireq_restore_handler);
|
||||
}
|
||||
@ -171,6 +174,8 @@ SCSIRequest* scsi_device_t::scsi_new_request(Bit32u tag)
|
||||
}
|
||||
r->tag = tag;
|
||||
r->sector_count = 0;
|
||||
r->async_supported = 0;
|
||||
r->seek_active = 0;
|
||||
r->buf_len = 0;
|
||||
r->status = 0;
|
||||
|
||||
@ -232,6 +237,8 @@ bx_bool scsi_device_t::save_requests(const char *path)
|
||||
fprintf(fp, " sector_count = %u\n", r->sector_count);
|
||||
fprintf(fp, " buf_len = %d\n", r->buf_len);
|
||||
fprintf(fp, " status = %u\n", r->status);
|
||||
fprintf(fp, " async_supported = %u\n", r->async_supported);
|
||||
fprintf(fp, " seek_active = %u\n", r->seek_active);
|
||||
fprintf(fp, "}\n");
|
||||
if (r->buf_len > 0) {
|
||||
sprintf(tmppath, "%s.%u", path, i);
|
||||
@ -323,6 +330,10 @@ void scsi_device_t::restore_requests(const char *path)
|
||||
r->buf_len = (int)value;
|
||||
} else if (!strcmp(pname, "status")) {
|
||||
r->status = (Bit32u)value;
|
||||
} else if (!strcmp(pname, "async_supported")) {
|
||||
r->async_supported = (bx_bool)value;
|
||||
} else if (!strcmp(pname, "seek_active")) {
|
||||
r->seek_active = (bx_bool)value;
|
||||
} else {
|
||||
BX_ERROR(("restore_requests(): data format error"));
|
||||
rrq_error = 1;
|
||||
@ -407,14 +418,19 @@ bx_bool scsi_device_t::scsi_read_data(Bit32u tag)
|
||||
n = SCSI_DMA_BUF_SIZE / (512 * cluster_size);
|
||||
r->buf_len = n * 512 * cluster_size;
|
||||
if (type == SCSIDEV_TYPE_CDROM) {
|
||||
i = 0;
|
||||
do {
|
||||
ret = (int)cdrom->read_block(r->dma_buf + (i * 2048), (Bit32u)(r->sector + i), 2048);
|
||||
} while ((++i < n) && (ret == 1));
|
||||
if (ret == 0) {
|
||||
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_MEDIUM_ERROR);
|
||||
if (r->async_supported) {
|
||||
if (!r->seek_active) start_seek(r);
|
||||
return 1;
|
||||
} else {
|
||||
scsi_read_complete((void*)r, 0);
|
||||
i = 0;
|
||||
do {
|
||||
ret = (int)cdrom->read_block(r->dma_buf + (i * 2048), (Bit32u)(r->sector + i), 2048);
|
||||
} while ((++i < n) && (ret == 1));
|
||||
if (ret == 0) {
|
||||
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_MEDIUM_ERROR);
|
||||
} else {
|
||||
scsi_read_complete((void*)r, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret = (int)hdimage->lseek(r->sector * 512, SEEK_SET);
|
||||
@ -511,7 +527,7 @@ Bit8u* scsi_device_t::scsi_get_buf(Bit32u tag)
|
||||
return r->dma_buf;
|
||||
}
|
||||
|
||||
Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, int lun)
|
||||
Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, int lun, bx_bool async)
|
||||
{
|
||||
Bit64u nb_sectors;
|
||||
Bit64u lba;
|
||||
@ -854,6 +870,7 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, int lun)
|
||||
goto illegal_lba;
|
||||
r->sector = lba;
|
||||
r->sector_count = len;
|
||||
r->async_supported = async;
|
||||
break;
|
||||
case 0x0a:
|
||||
case 0x2a:
|
||||
@ -989,26 +1006,70 @@ void scsi_device_t::set_inserted(bx_bool value)
|
||||
inserted = value;
|
||||
if (inserted) {
|
||||
max_lba = cdrom->capacity() - 1;
|
||||
curr_lba = max_lba;
|
||||
} else {
|
||||
max_lba = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void scsi_device_t::start_seek(SCSIRequest *r)
|
||||
{
|
||||
Bit64s new_pos, prev_pos, max_pos;
|
||||
Bit32u seek_time;
|
||||
double fSeekBase, fSeekTime;
|
||||
|
||||
max_pos = max_lba;
|
||||
prev_pos = curr_lba;
|
||||
new_pos = r->sector + r->sector_count;
|
||||
if (type == SCSIDEV_TYPE_CDROM) {
|
||||
fSeekBase = 80000.0;
|
||||
} else {
|
||||
fSeekBase = 5000.0;
|
||||
}
|
||||
fSeekTime = fSeekBase * (double)abs((int)(new_pos - prev_pos + 1)) / (max_pos + 1);
|
||||
seek_time = (fSeekTime > 10.0) ? (Bit32u)fSeekTime : 10;
|
||||
bx_pc_system.activate_timer(seek_timer_index, seek_time, 0);
|
||||
bx_pc_system.setTimerParam(seek_timer_index, r->tag);
|
||||
r->seek_active = 1;
|
||||
}
|
||||
|
||||
void scsi_device_t::seek_timer_handler(void *this_ptr)
|
||||
{
|
||||
scsi_device_t *class_ptr = (scsi_device_t *) this_ptr;
|
||||
class_ptr->seek_timer();
|
||||
}
|
||||
|
||||
void scsi_device_t::seek_timer()
|
||||
{
|
||||
Bit32u i, n, tag = bx_pc_system.triggeredTimerParam();
|
||||
SCSIRequest *r = scsi_find_request(tag);
|
||||
int ret = 0;
|
||||
|
||||
n = r->sector_count;
|
||||
if (n > (Bit32u)(SCSI_DMA_BUF_SIZE / (512 * cluster_size)))
|
||||
n = SCSI_DMA_BUF_SIZE / (512 * cluster_size);
|
||||
if (type == SCSIDEV_TYPE_CDROM) {
|
||||
i = 0;
|
||||
curr_lba = r->sector;
|
||||
do {
|
||||
ret = (int)cdrom->read_block(r->dma_buf + (i * 2048), (Bit32u)(r->sector + i), 2048);
|
||||
curr_lba++;
|
||||
} while ((++i < n) && (ret == 1));
|
||||
if (ret == 0) {
|
||||
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_MEDIUM_ERROR);
|
||||
} else {
|
||||
scsi_read_complete((void*)r, 0);
|
||||
}
|
||||
r->sector += n;
|
||||
r->sector_count -= n;
|
||||
r->seek_active = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Turn on BX_DEBUG messages at connection time
|
||||
void scsi_device_t::set_debug_mode()
|
||||
{
|
||||
setonoff(LOGLEV_DEBUG, ACT_REPORT);
|
||||
}
|
||||
|
||||
void scsi_device_t::seek_timer()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
#endif // BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB
|
||||
|
@ -61,6 +61,8 @@ typedef struct SCSIRequest {
|
||||
int buf_len;
|
||||
Bit8u *dma_buf;
|
||||
Bit32u status;
|
||||
bx_bool async_supported;
|
||||
bx_bool seek_active;
|
||||
struct SCSIRequest *next;
|
||||
} SCSIRequest;
|
||||
|
||||
@ -74,7 +76,7 @@ public:
|
||||
virtual ~scsi_device_t(void);
|
||||
|
||||
void register_state(bx_list_c *parent, const char *name);
|
||||
Bit32s scsi_send_command(Bit32u tag, Bit8u *buf, int lun);
|
||||
Bit32s scsi_send_command(Bit32u tag, Bit8u *buf, int lun, bx_bool async);
|
||||
void scsi_command_complete(SCSIRequest *r, int status, int sense);
|
||||
void scsi_cancel_io(Bit32u tag);
|
||||
void scsi_read_complete(void *req, int ret);
|
||||
@ -87,7 +89,6 @@ public:
|
||||
bx_bool get_inserted() {return inserted;}
|
||||
bx_bool get_locked() {return locked;}
|
||||
static void seek_timer_handler(void *);
|
||||
void seek_timer(void);
|
||||
bx_bool save_requests(const char *path);
|
||||
void restore_requests(const char *path);
|
||||
void set_debug_mode();
|
||||
@ -98,12 +99,16 @@ protected:
|
||||
SCSIRequest *scsi_find_request(Bit32u tag);
|
||||
|
||||
private:
|
||||
void start_seek(SCSIRequest *r);
|
||||
void seek_timer(void);
|
||||
|
||||
enum scsidev_type type;
|
||||
device_image_t *hdimage;
|
||||
cdrom_base_c *cdrom;
|
||||
SCSIRequest *requests;
|
||||
int cluster_size;
|
||||
Bit64u max_lba;
|
||||
Bit64u curr_lba;
|
||||
int sense;
|
||||
int tcq;
|
||||
scsi_completionfn completion;
|
||||
|
@ -117,6 +117,10 @@
|
||||
// USB 3.0
|
||||
#define USB_DT_BIN_DEV_OBJ_STORE 0x0F
|
||||
|
||||
typedef struct USBPacket USBPacket;
|
||||
|
||||
typedef void USBCallback(USBPacket *packet, void *dev);
|
||||
|
||||
class usb_device_c;
|
||||
|
||||
struct USBPacket {
|
||||
@ -125,6 +129,8 @@ struct USBPacket {
|
||||
Bit8u devep;
|
||||
Bit8u *data;
|
||||
int len;
|
||||
USBCallback *complete_cb;
|
||||
void *complete_dev;
|
||||
usb_device_c *dev;
|
||||
};
|
||||
|
||||
@ -213,4 +219,9 @@ static BX_CPP_INLINE void usb_cancel_packet(USBPacket *p)
|
||||
p->dev->cancel_packet(p);
|
||||
}
|
||||
|
||||
static BX_CPP_INLINE void usb_packet_complete(USBPacket *p)
|
||||
{
|
||||
p->complete_cb(p, p->complete_dev);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -615,6 +615,7 @@ int usb_msd_device_c::handle_data(USBPacket *p)
|
||||
Bit8u devep = p->devep;
|
||||
Bit8u *data = p->data;
|
||||
int len = p->len;
|
||||
bx_bool async = (p->complete_cb != NULL);
|
||||
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
@ -646,7 +647,7 @@ int usb_msd_device_c::handle_data(USBPacket *p)
|
||||
BX_DEBUG(("command tag 0x%X flags %08X len %d data %d",
|
||||
s.tag, cbw.flags, cbw.cmd_len, s.data_len));
|
||||
s.residue = 0;
|
||||
s.scsi_dev->scsi_send_command(s.tag, cbw.cmd, cbw.lun);
|
||||
s.scsi_dev->scsi_send_command(s.tag, cbw.cmd, cbw.lun, async);
|
||||
if (s.residue == 0) {
|
||||
if (s.mode == USB_MSDM_DATAIN) {
|
||||
if (s.scsi_dev->scsi_read_data(s.tag)) {
|
||||
@ -841,6 +842,7 @@ void usb_msd_device_c::command_complete(int reason, Bit32u tag, Bit32u arg)
|
||||
s.mode = USB_MSDM_CSW;
|
||||
}
|
||||
s.packet = NULL;
|
||||
usb_packet_complete(p);
|
||||
} else if (s.data_len == 0) {
|
||||
s.mode = USB_MSDM_CSW;
|
||||
}
|
||||
@ -853,6 +855,7 @@ void usb_msd_device_c::command_complete(int reason, Bit32u tag, Bit32u arg)
|
||||
if (s.usb_len == 0) {
|
||||
BX_INFO(("packet complete %p", p));
|
||||
s.packet = NULL;
|
||||
usb_packet_complete(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -174,7 +174,7 @@ void bx_usb_ohci_c::init(void)
|
||||
return;
|
||||
}
|
||||
|
||||
BX_OHCI_THIS device_buffer = new Bit8u[65536];
|
||||
BX_OHCI_THIS device_buffer = new Bit8u[8192];
|
||||
|
||||
// Call our frame timer routine every 1mS (1,000uS)
|
||||
// Continuous and active
|
||||
@ -371,6 +371,11 @@ void bx_usb_ohci_c::reset_hc()
|
||||
usb_set_connect_status(i, BX_OHCI_THIS hub.usb_port[i].device->get_type(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (BX_OHCI_THIS hub.async_td) {
|
||||
usb_cancel_packet(&BX_OHCI_THIS usb_packet);
|
||||
BX_OHCI_THIS hub.async_td = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void bx_usb_ohci_c::reset_port(int p)
|
||||
@ -471,6 +476,8 @@ void bx_usb_ohci_c::register_state(void)
|
||||
new bx_shadow_bool_c(hub, "use_control_head", &BX_OHCI_THIS hub.use_control_head);
|
||||
new bx_shadow_bool_c(hub, "use_bulk_head", &BX_OHCI_THIS hub.use_bulk_head);
|
||||
new bx_shadow_num_c(hub, "sof_time", &BX_OHCI_THIS hub.sof_time);
|
||||
new bx_shadow_num_c(hub, "async_td", &BX_OHCI_THIS hub.async_td);
|
||||
new bx_shadow_bool_c(hub, "async_complete", &BX_OHCI_THIS hub.async_complete);
|
||||
register_pci_state(hub);
|
||||
}
|
||||
|
||||
@ -1078,55 +1085,8 @@ void bx_usb_ohci_c::usb_frame_timer(void)
|
||||
if ((BX_OHCI_THIS hub.ohci_done_count != 7) && (BX_OHCI_THIS hub.ohci_done_count > 0))
|
||||
BX_OHCI_THIS hub.ohci_done_count--;
|
||||
|
||||
// TODO: Rather than just comparing .fr to <8000 here, and <4000 below, see the highlighted
|
||||
// statement on page 45.
|
||||
BX_OHCI_THIS process_lists();
|
||||
|
||||
// if the control list is enabled *and* the control list filled bit is set, do a control list ED
|
||||
if (BX_OHCI_THIS hub.op_regs.HcControl.cle) {
|
||||
if (BX_OHCI_THIS hub.use_control_head) {
|
||||
BX_OHCI_THIS hub.op_regs.HcControlCurrentED = 0;
|
||||
BX_OHCI_THIS hub.use_control_head = 0;
|
||||
}
|
||||
if (!BX_OHCI_THIS hub.op_regs.HcControlCurrentED && BX_OHCI_THIS hub.op_regs.HcCommandStatus.clf) {
|
||||
BX_OHCI_THIS hub.op_regs.HcControlCurrentED = BX_OHCI_THIS hub.op_regs.HcControlHeadED;
|
||||
BX_OHCI_THIS hub.op_regs.HcCommandStatus.clf = 0;
|
||||
}
|
||||
while (BX_OHCI_THIS hub.op_regs.HcControlCurrentED) {
|
||||
DEV_MEM_READ_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcControlCurrentED, 4, (Bit8u*) &cur_ed.dword0);
|
||||
DEV_MEM_READ_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcControlCurrentED + 4, 4, (Bit8u*) &cur_ed.dword1);
|
||||
DEV_MEM_READ_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcControlCurrentED + 8, 4, (Bit8u*) &cur_ed.dword2);
|
||||
DEV_MEM_READ_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcControlCurrentED + 12, 4, (Bit8u*) &cur_ed.dword3);
|
||||
process_ed(&cur_ed, BX_OHCI_THIS hub.op_regs.HcControlCurrentED);
|
||||
BX_OHCI_THIS hub.op_regs.HcControlCurrentED = ED_GET_NEXTED(&cur_ed);
|
||||
if (get_frame_remaining() < 8000)
|
||||
goto do_bulk_eds;
|
||||
}
|
||||
}
|
||||
|
||||
do_bulk_eds:
|
||||
// if the bulk list is enabled *and* the bulk list filled bit is set, do a bulk list ED
|
||||
if (BX_OHCI_THIS hub.op_regs.HcControl.ble) {
|
||||
if (BX_OHCI_THIS hub.use_bulk_head) {
|
||||
BX_OHCI_THIS hub.op_regs.HcBulkCurrentED = 0;
|
||||
BX_OHCI_THIS hub.use_bulk_head = 0;
|
||||
}
|
||||
if (!BX_OHCI_THIS hub.op_regs.HcBulkCurrentED && BX_OHCI_THIS hub.op_regs.HcCommandStatus.blf) {
|
||||
BX_OHCI_THIS hub.op_regs.HcBulkCurrentED = BX_OHCI_THIS hub.op_regs.HcBulkHeadED;
|
||||
BX_OHCI_THIS hub.op_regs.HcCommandStatus.blf = 0;
|
||||
}
|
||||
while (BX_OHCI_THIS hub.op_regs.HcBulkCurrentED) {
|
||||
DEV_MEM_READ_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcBulkCurrentED, 4, (Bit8u*) &cur_ed.dword0);
|
||||
DEV_MEM_READ_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcBulkCurrentED + 4, 4, (Bit8u*) &cur_ed.dword1);
|
||||
DEV_MEM_READ_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcBulkCurrentED + 8, 4, (Bit8u*) &cur_ed.dword2);
|
||||
DEV_MEM_READ_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcBulkCurrentED + 12, 4, (Bit8u*) &cur_ed.dword3);
|
||||
process_ed(&cur_ed, BX_OHCI_THIS hub.op_regs.HcBulkCurrentED);
|
||||
BX_OHCI_THIS hub.op_regs.HcBulkCurrentED = ED_GET_NEXTED(&cur_ed);
|
||||
if (get_frame_remaining() < 4000)
|
||||
goto do_iso_eds;
|
||||
}
|
||||
}
|
||||
|
||||
do_iso_eds:
|
||||
// do the ED's in the interrupt table
|
||||
if (BX_OHCI_THIS hub.op_regs.HcControl.ple) {
|
||||
address = BX_OHCI_THIS hub.op_regs.HcHCCA + ((BX_OHCI_THIS hub.op_regs.HcFmNumber & 0x1F) * 4);
|
||||
@ -1144,6 +1104,58 @@ do_iso_eds:
|
||||
} // end run schedule
|
||||
}
|
||||
|
||||
void bx_usb_ohci_c::process_lists(void)
|
||||
{
|
||||
struct OHCI_ED cur_ed;
|
||||
|
||||
// TODO: Rather than just comparing .fr to <8000 here, and <4000 below, see the highlighted
|
||||
// statement on page 45.
|
||||
|
||||
// if the control list is enabled *and* the control list filled bit is set, do a control list ED
|
||||
if (BX_OHCI_THIS hub.op_regs.HcControl.cle) {
|
||||
if (BX_OHCI_THIS hub.use_control_head) {
|
||||
BX_OHCI_THIS hub.op_regs.HcControlCurrentED = 0;
|
||||
BX_OHCI_THIS hub.use_control_head = 0;
|
||||
}
|
||||
if (!BX_OHCI_THIS hub.op_regs.HcControlCurrentED && BX_OHCI_THIS hub.op_regs.HcCommandStatus.clf) {
|
||||
BX_OHCI_THIS hub.op_regs.HcControlCurrentED = BX_OHCI_THIS hub.op_regs.HcControlHeadED;
|
||||
BX_OHCI_THIS hub.op_regs.HcCommandStatus.clf = 0;
|
||||
}
|
||||
while (BX_OHCI_THIS hub.op_regs.HcControlCurrentED) {
|
||||
DEV_MEM_READ_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcControlCurrentED, 4, (Bit8u*) &cur_ed.dword0);
|
||||
DEV_MEM_READ_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcControlCurrentED + 4, 4, (Bit8u*) &cur_ed.dword1);
|
||||
DEV_MEM_READ_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcControlCurrentED + 8, 4, (Bit8u*) &cur_ed.dword2);
|
||||
DEV_MEM_READ_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcControlCurrentED + 12, 4, (Bit8u*) &cur_ed.dword3);
|
||||
process_ed(&cur_ed, BX_OHCI_THIS hub.op_regs.HcControlCurrentED);
|
||||
BX_OHCI_THIS hub.op_regs.HcControlCurrentED = ED_GET_NEXTED(&cur_ed);
|
||||
if (get_frame_remaining() < 8000)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if the bulk list is enabled *and* the bulk list filled bit is set, do a bulk list ED
|
||||
if (BX_OHCI_THIS hub.op_regs.HcControl.ble) {
|
||||
if (BX_OHCI_THIS hub.use_bulk_head) {
|
||||
BX_OHCI_THIS hub.op_regs.HcBulkCurrentED = 0;
|
||||
BX_OHCI_THIS hub.use_bulk_head = 0;
|
||||
}
|
||||
if (!BX_OHCI_THIS hub.op_regs.HcBulkCurrentED && BX_OHCI_THIS hub.op_regs.HcCommandStatus.blf) {
|
||||
BX_OHCI_THIS hub.op_regs.HcBulkCurrentED = BX_OHCI_THIS hub.op_regs.HcBulkHeadED;
|
||||
BX_OHCI_THIS hub.op_regs.HcCommandStatus.blf = 0;
|
||||
}
|
||||
while (BX_OHCI_THIS hub.op_regs.HcBulkCurrentED) {
|
||||
DEV_MEM_READ_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcBulkCurrentED, 4, (Bit8u*) &cur_ed.dword0);
|
||||
DEV_MEM_READ_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcBulkCurrentED + 4, 4, (Bit8u*) &cur_ed.dword1);
|
||||
DEV_MEM_READ_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcBulkCurrentED + 8, 4, (Bit8u*) &cur_ed.dword2);
|
||||
DEV_MEM_READ_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcBulkCurrentED + 12, 4, (Bit8u*) &cur_ed.dword3);
|
||||
process_ed(&cur_ed, BX_OHCI_THIS hub.op_regs.HcBulkCurrentED);
|
||||
BX_OHCI_THIS hub.op_regs.HcBulkCurrentED = ED_GET_NEXTED(&cur_ed);
|
||||
if (get_frame_remaining() < 4000)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bx_usb_ohci_c::process_ed(struct OHCI_ED *ed, const Bit32u ed_address)
|
||||
{
|
||||
struct OHCI_TD cur_td;
|
||||
@ -1184,10 +1196,35 @@ void bx_usb_ohci_c::process_ed(struct OHCI_ED *ed, const Bit32u ed_address)
|
||||
}
|
||||
}
|
||||
|
||||
void ohci_async_complete_packet(USBPacket *packet, void *dev)
|
||||
{
|
||||
((bx_usb_ohci_c*)dev)->async_complete_packet(packet);
|
||||
}
|
||||
|
||||
void bx_usb_ohci_c::async_complete_packet(USBPacket *packet)
|
||||
{
|
||||
BX_INFO(("Experimental async packet completion"));
|
||||
BX_OHCI_THIS hub.async_complete = 1;
|
||||
// These hacks are currently required for async completion
|
||||
BX_OHCI_THIS hub.use_control_head = 1;
|
||||
BX_OHCI_THIS hub.use_bulk_head = 1;
|
||||
BX_OHCI_THIS hub.op_regs.HcCommandStatus.blf = 1;
|
||||
BX_OHCI_THIS hub.op_regs.HcCommandStatus.clf = 1;
|
||||
BX_OHCI_THIS process_lists();
|
||||
}
|
||||
|
||||
bx_bool bx_usb_ohci_c::process_td(struct OHCI_TD *td, struct OHCI_ED *ed)
|
||||
{
|
||||
unsigned pid = 0, len = 0, len1, len2;
|
||||
int ilen, ret = 0;
|
||||
Bit32u addr;
|
||||
bx_bool completion;
|
||||
|
||||
addr = ED_GET_HEADP(ed);
|
||||
completion = (addr == BX_OHCI_THIS hub.async_td);
|
||||
if (completion && !BX_OHCI_THIS hub.async_complete) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The td->cc field should be 111x if it hasn't been processed yet.
|
||||
if (TD_GET_CC(td) < NotAccessed) {
|
||||
@ -1222,62 +1259,75 @@ bx_bool bx_usb_ohci_c::process_td(struct OHCI_TD *td, struct OHCI_ED *ed)
|
||||
} else
|
||||
len = 0;
|
||||
|
||||
BX_OHCI_THIS usb_packet.pid = pid;
|
||||
BX_OHCI_THIS usb_packet.devaddr = ED_GET_FA(ed);
|
||||
BX_OHCI_THIS usb_packet.devep = ED_GET_EN(ed);
|
||||
BX_OHCI_THIS usb_packet.data = BX_OHCI_THIS device_buffer;
|
||||
switch (pid) {
|
||||
case USB_TOKEN_SETUP:
|
||||
case USB_TOKEN_OUT:
|
||||
BX_OHCI_THIS usb_packet.len = (len <= ED_GET_MPS(ed)) ? len : ED_GET_MPS(ed);
|
||||
break;
|
||||
case USB_TOKEN_IN:
|
||||
BX_OHCI_THIS usb_packet.len = len;
|
||||
break;
|
||||
if (completion) {
|
||||
ret = BX_OHCI_THIS usb_packet.len;
|
||||
BX_OHCI_THIS hub.async_td = 0;
|
||||
BX_OHCI_THIS hub.async_complete = 0;
|
||||
} else {
|
||||
if (BX_OHCI_THIS hub.async_td) {
|
||||
BX_ERROR(("too many pending packets"));
|
||||
return 0;
|
||||
}
|
||||
BX_OHCI_THIS usb_packet.pid = pid;
|
||||
BX_OHCI_THIS usb_packet.devaddr = ED_GET_FA(ed);
|
||||
BX_OHCI_THIS usb_packet.devep = ED_GET_EN(ed);
|
||||
BX_OHCI_THIS usb_packet.data = BX_OHCI_THIS device_buffer;
|
||||
BX_OHCI_THIS usb_packet.complete_cb = ohci_async_complete_packet;
|
||||
BX_OHCI_THIS usb_packet.complete_dev = this;
|
||||
switch (pid) {
|
||||
case USB_TOKEN_SETUP:
|
||||
case USB_TOKEN_OUT:
|
||||
BX_OHCI_THIS usb_packet.len = (len <= ED_GET_MPS(ed)) ? len : ED_GET_MPS(ed);
|
||||
break;
|
||||
case USB_TOKEN_IN:
|
||||
BX_OHCI_THIS usb_packet.len = len;
|
||||
break;
|
||||
}
|
||||
|
||||
BX_DEBUG((" pid = %s addr = %i endpnt = %i len = %i mps = %i (td->cbp = 0x%08X, td->be = 0x%08X)",
|
||||
(pid == USB_TOKEN_IN)? "IN" : (pid == USB_TOKEN_OUT) ? "OUT" : (pid == USB_TOKEN_SETUP) ? "SETUP" : "UNKNOWN",
|
||||
ED_GET_FA(ed), ED_GET_EN(ed), len, ED_GET_MPS(ed), TD_GET_CBP(td), TD_GET_BE(td)));
|
||||
BX_DEBUG((" td->t = %i ed->c = %i td->di = %i td->r = %i", TD_GET_T(td), ED_GET_C(ed), TD_GET_DI(td), TD_GET_R(td)));
|
||||
|
||||
switch (pid) {
|
||||
case USB_TOKEN_SETUP:
|
||||
if (len > 0)
|
||||
DEV_MEM_READ_PHYSICAL_DMA(TD_GET_CBP(td), len, device_buffer);
|
||||
// TODO: This is a hack. dev->handle_packet() should return the amount of bytes
|
||||
// it received, not the amount it anticipates on receiving/sending in the next packet.
|
||||
if ((ret = BX_OHCI_THIS broadcast_packet(&BX_OHCI_THIS usb_packet)) >= 0)
|
||||
ret = 8;
|
||||
break;
|
||||
case USB_TOKEN_OUT:
|
||||
if (len > 0)
|
||||
DEV_MEM_READ_PHYSICAL_DMA(TD_GET_CBP(td), len, device_buffer);
|
||||
ret = BX_OHCI_THIS broadcast_packet(&BX_OHCI_THIS usb_packet);
|
||||
break;
|
||||
case USB_TOKEN_IN:
|
||||
ret = BX_OHCI_THIS broadcast_packet(&BX_OHCI_THIS usb_packet);
|
||||
break;
|
||||
default:
|
||||
TD_SET_CC(td, UnexpectedPID);
|
||||
TD_SET_EC(td, 3);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
BX_OHCI_THIS hub.async_td = addr;
|
||||
BX_INFO(("Experimental async packet handling"));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
BX_DEBUG((" pid = %s addr = %i endpnt = %i len = %i mps = %i (td->cbp = 0x%08X, td->be = 0x%08X)",
|
||||
(pid == USB_TOKEN_IN)? "IN" : (pid == USB_TOKEN_OUT) ? "OUT" : (pid == USB_TOKEN_SETUP) ? "SETUP" : "UNKNOWN",
|
||||
ED_GET_FA(ed), ED_GET_EN(ed), len, ED_GET_MPS(ed), TD_GET_CBP(td), TD_GET_BE(td)));
|
||||
BX_DEBUG((" td->t = %i ed->c = %i td->di = %i td->r = %i", TD_GET_T(td), ED_GET_C(ed), TD_GET_DI(td), TD_GET_R(td)));
|
||||
|
||||
switch (pid) {
|
||||
case USB_TOKEN_SETUP:
|
||||
if (len > 0)
|
||||
DEV_MEM_READ_PHYSICAL_DMA(TD_GET_CBP(td), len, device_buffer);
|
||||
// TODO: This is a hack. dev->handle_packet() should return the amount of bytes
|
||||
// it received, not the amount it anticipates on receiving/sending in the next packet.
|
||||
if ((ret = BX_OHCI_THIS broadcast_packet(&BX_OHCI_THIS usb_packet)) >= 0)
|
||||
ret = 8;
|
||||
break;
|
||||
case USB_TOKEN_OUT:
|
||||
if (len > 0)
|
||||
DEV_MEM_READ_PHYSICAL_DMA(TD_GET_CBP(td), len, device_buffer);
|
||||
ret = BX_OHCI_THIS broadcast_packet(&BX_OHCI_THIS usb_packet);
|
||||
break;
|
||||
case USB_TOKEN_IN:
|
||||
ret = BX_OHCI_THIS broadcast_packet(&BX_OHCI_THIS usb_packet);
|
||||
if (ret > 0) {
|
||||
if (((TD_GET_CBP(td) & 0xfff) + ret) > 0x1000) {
|
||||
len1 = 0x1000 - (TD_GET_CBP(td) & 0xfff);
|
||||
len2 = ret - len1;
|
||||
DEV_MEM_WRITE_PHYSICAL_DMA(TD_GET_CBP(td), len1, device_buffer);
|
||||
DEV_MEM_WRITE_PHYSICAL_DMA((TD_GET_BE(td) & ~0xfff), len2, device_buffer+len1);
|
||||
} else {
|
||||
DEV_MEM_WRITE_PHYSICAL_DMA(TD_GET_CBP(td), ret, device_buffer);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
TD_SET_CC(td, UnexpectedPID);
|
||||
TD_SET_EC(td, 3);
|
||||
return 1;
|
||||
if ((ret > 0) && (pid == USB_TOKEN_IN)) {
|
||||
if (((TD_GET_CBP(td) & 0xfff) + ret) > 0x1000) {
|
||||
len1 = 0x1000 - (TD_GET_CBP(td) & 0xfff);
|
||||
len2 = ret - len1;
|
||||
DEV_MEM_WRITE_PHYSICAL_DMA(TD_GET_CBP(td), len1, device_buffer);
|
||||
DEV_MEM_WRITE_PHYSICAL_DMA((TD_GET_BE(td) & ~0xfff), len2, device_buffer+len1);
|
||||
} else {
|
||||
DEV_MEM_WRITE_PHYSICAL_DMA(TD_GET_CBP(td), ret, device_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
BX_ERROR(("Async packet handling not implemented yet"));
|
||||
}
|
||||
|
||||
if ((ret == (int)len) || ((pid == USB_TOKEN_IN) && (ret >= 0) &&
|
||||
TD_GET_R(td)) || ((pid == USB_TOKEN_OUT) && (ret >= 0) &&
|
||||
(ret <= (int) ED_GET_MPS(ed)))) {
|
||||
@ -1317,9 +1367,6 @@ bx_bool bx_usb_ohci_c::process_td(struct OHCI_TD *td, struct OHCI_ED *ed)
|
||||
case USB_RET_BABBLE: // (-4)
|
||||
TD_SET_CC(td, BufferOverrun);
|
||||
break;
|
||||
case USB_RET_ASYNC: // (-5)
|
||||
TD_SET_CC(td, BufferOverrun);
|
||||
break;
|
||||
default:
|
||||
BX_ERROR(("Unknown error returned: %i", ret));
|
||||
break;
|
||||
|
@ -235,6 +235,8 @@ typedef struct {
|
||||
bx_bool use_control_head;
|
||||
bx_bool use_bulk_head;
|
||||
Bit64u sof_time;
|
||||
Bit32u async_td;
|
||||
bx_bool async_complete;
|
||||
|
||||
Bit8u device_change;
|
||||
int rt_conf_id;
|
||||
@ -253,6 +255,8 @@ public:
|
||||
virtual Bit32u pci_read_handler(Bit8u address, unsigned io_len);
|
||||
virtual void pci_write_handler(Bit8u address, Bit32u value, unsigned io_len);
|
||||
|
||||
void async_complete_packet(USBPacket *packet);
|
||||
|
||||
static const char *usb_param_handler(bx_param_string_c *param, int set,
|
||||
const char *oldval, const char *val, int maxlen);
|
||||
|
||||
@ -278,6 +282,7 @@ private:
|
||||
|
||||
static Bit32u get_frame_remaining(void);
|
||||
|
||||
void process_lists();
|
||||
void process_ed(struct OHCI_ED *, const Bit32u);
|
||||
bx_bool process_td(struct OHCI_TD *, struct OHCI_ED *);
|
||||
|
||||
|
@ -921,6 +921,8 @@ bx_bool bx_usb_uhci_c::DoTransfer(Bit32u address, Bit32u queue_num, struct TD *t
|
||||
BX_UHCI_THIS usb_packet.devep = endpt;
|
||||
BX_UHCI_THIS usb_packet.data = device_buffer;
|
||||
BX_UHCI_THIS usb_packet.len = maxlen;
|
||||
BX_UHCI_THIS usb_packet.complete_cb = NULL;
|
||||
BX_UHCI_THIS usb_packet.complete_dev = this;
|
||||
switch (pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
case USB_TOKEN_SETUP:
|
||||
|
@ -2007,6 +2007,8 @@ void bx_usb_xhci_c::process_transfer_ring(const int slot, const int ep)
|
||||
packet.devep = (ep >> 1);
|
||||
packet.data = BX_XHCI_THIS device_buffer;
|
||||
packet.len = transfer_length;
|
||||
packet.complete_cb = NULL;
|
||||
packet.complete_dev = BX_XHCI_THIS_PTR;
|
||||
switch (cur_direction) {
|
||||
case USB_TOKEN_OUT:
|
||||
case USB_TOKEN_SETUP:
|
||||
@ -2818,6 +2820,8 @@ int bx_usb_xhci_c::send_set_address(const int addr, const int port_num)
|
||||
packet.devaddr = 0; // default address
|
||||
packet.len = 8;
|
||||
packet.data = setup_address;
|
||||
packet.complete_cb = NULL;
|
||||
packet.complete_dev = BX_XHCI_THIS_PTR;
|
||||
ret = BX_XHCI_THIS broadcast_packet(&packet, port_num);
|
||||
if (ret == 0) {
|
||||
packet.pid = USB_TOKEN_IN;
|
||||
|
Loading…
Reference in New Issue
Block a user