⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ohci.c

📁 newos is new operation system
💻 C
📖 第 1 页 / 共 2 页
字号:
/*** Copyright 2003, Travis Geiselbrecht. All rights reserved.** Distributed under the terms of the NewOS License.*/#include <kernel/kernel.h>#include <kernel/debug.h>#include <kernel/heap.h>#include <kernel/vm.h>#include <kernel/thread.h>#include <kernel/sem.h>#include <kernel/module.h>#include <kernel/int.h>#include <string.h>#include <kernel/bus/pci/pci.h>#include <kernel/bus/usb/usb.h>#include <kernel/bus/usb/usb_hc.h>#include "ohci.h"#define debug_level_flow 6#define debug_level_error 10#define debug_level_info 10#define DEBUG_MSG_PREFIX "OHCI - "#include <kernel/debug_ext.h>#define phys_to_virt(oi, phys) (((addr_t)(phys) - (oi)->hcca_phys) + (addr_t)(oi)->hcca)static ohci *oi_list = NULL;static ohci_td *allocate_td(ohci *oi){	ohci_td *td;	// pull a td from the freelist	mutex_lock(&oi->td_freelist_lock);	td = oi->td_freelist;	if(td)		oi->td_freelist = td->next;	mutex_unlock(&oi->td_freelist_lock);	if(!td)		return NULL;	td->flags = 0;	td->curr_buf_ptr = 0;	td->next_td = 0;	td->buf_end = 0;	return td;}static void free_td(ohci *oi, ohci_td *td){	mutex_lock(&oi->td_freelist_lock);	td->next = oi->td_freelist;	oi->td_freelist = td;	mutex_unlock(&oi->td_freelist_lock);}static ohci_ed *create_ed(void){	ohci_ed *ed;	ed = kmalloc(sizeof(ohci_ed));	if(!ed)		return NULL;	memset(ed, 0, sizeof(ohci_ed));	vm_get_page_mapping(vm_get_kernel_aspace_id(), (addr_t)ed, &ed->phys_addr);	return ed;}static void enqueue_ed(ohci_ed *ed){	ohci_queue *queue = ed->queue;	ohci *oi = ed->oi;	mutex_lock(&oi->hc_list_lock);	// stick it in our queue	ed->prev_ed = NULL;	ed->next_ed = queue->head;	if(ed->next_ed != NULL)		ed->next_ed->prev_ed = ed;	queue->head = ed;	// put it in the hardware's queue	ed->next = *queue->head_phys;	*queue->head_phys = (uint32)ed->phys_addr;	mutex_unlock(&oi->hc_list_lock);}static ohci_ed *create_endpoint(ohci *oi, usb_endpoint_descriptor *endpoint, int address, bool lowspeed){	ohci_ed *ed;	ohci_td *td;	ed = create_ed();	if(!ed)		return ed;	// save a pointer to the original usb endpoint structure	ed->usb_ed = endpoint;	ed->oi = oi;	// figure out what queue it should be in	switch(endpoint->attributes & 0x3) {		case USB_ENDPOINT_ATTR_CONTROL:			ed->queue = &oi->control_queue;			break;		case USB_ENDPOINT_ATTR_ISO:			// not supported			break;		case USB_ENDPOINT_ATTR_BULK:			ed->queue = &oi->bulk_queue;			break;		case USB_ENDPOINT_ATTR_INT:			ed->queue = &oi->interrupt_queue[INT_Q_32MS]; // XXX find the correct queue and rotate through them			break;	}	td = allocate_td(oi);	// set some hardware flags based on the usb endpoint's data	ed->flags = (endpoint->max_packet_size << 16) | ((endpoint->endpoint_address & 0xf) << 7) | (address & 0x7f);	if((endpoint->attributes & 0x3) == USB_ENDPOINT_ATTR_CONTROL) {		ed->flags |= OHCI_ED_FLAGS_DIR_TD;	} else {		if(endpoint->endpoint_address & 0x80)			ed->flags |= OHCI_ED_FLAGS_DIR_IN;		else			ed->flags |= OHCI_ED_FLAGS_DIR_OUT;	}	if(lowspeed)		ed->flags |= OHCI_ED_FLAGS_SPEED;	// stick the null transfer descriptor in our endpoint descriptors list	ed->tail_td = td;	ed->tail = ed->head = td->phys_addr;	ed->prev_ed = ed->next_ed = NULL;	// enqueue this descriptor	enqueue_ed(ed);	return ed;}static int ohci_done_list_processor(void *_oi){	ohci *oi = (ohci *)_oi;	ohci_td *td;	ohci_td *done_list;	addr_t td_phys;	for(;;) {		sem_acquire(oi->done_list_sem, 1);		if(oi->hcca->done_head != 0)			SHOW_FLOW(6, "WDH 0x%x (0x%lx)", oi->hcca->done_head, phys_to_virt(oi, oi->hcca->done_head));		done_list = NULL;		// pull all of the entries from the done list, put them in another list in reverse order		td_phys = oi->hcca->done_head & 0xfffffff0;		while(td_phys != 0) {			td = (ohci_td *)phys_to_virt(oi, td_phys);			td_phys = td->next_td;			td->next = done_list;			done_list = td;		}		// ack the interrupt		oi->hcca->done_head = 0;		oi->regs->interrupt_status = INT_WDH;		td = done_list;		while(done_list) {			td = done_list;			bool transfer_complete = false;			SHOW_FLOW(6, "dealing with %p: CC 0x%x", td, OHCI_TD_FLAGS_CC(td->flags));			if(OHCI_TD_FLAGS_CC(td->flags) == OHCI_CC_NoError) {				if(td->last_in_transfer) {					SHOW_FLOW(6, "transfer %p now finished", td->usb_transfer);					td->usb_transfer->status = NO_ERROR;					transfer_complete = true;				}			} else {				// there was an error, the endpoint is halted				td->usb_transfer->status = ERR_GENERAL; // XXX make smarter				transfer_complete = true;				SHOW_FLOW(6, "stalled transfer, part of usb transfer %p", td->usb_transfer);				// walk the head pointer of this endpoint and remove the rest of this transfer's descriptors				// the next one after this one should be at the head of the list				td_phys = td->ed->head & 0xfffffff0;				while(td_phys != 0) {					ohci_td *temp_td = (ohci_td *)phys_to_virt(oi, td_phys);					SHOW_FLOW(6, "stalled transfer at %p (0x%lx), usb transfer %p", temp_td, td_phys, temp_td->usb_transfer);					if(temp_td->usb_transfer != td->usb_transfer)						break;					// this transfer descriptor is part of the errored transfer					td_phys = temp_td->next_td;					free_td(oi, temp_td);				}				SHOW_FLOW(6, "setting head to 0x%lx, tail is at 0x%x", td_phys, td->ed->tail);				td->ed->head = td_phys; // set the new head pointer				oi->regs->command_status = COMMAND_CLF | COMMAND_BLF;			}			if(transfer_complete) {				if(td->usb_transfer->callback != NULL)					td->usb_transfer->callback(td->usb_transfer, td->usb_transfer->cookie);				if(td->usb_transfer->completion_sem >= 0)					sem_release(td->usb_transfer->completion_sem, 1);			}			done_list = td->next;			free_td(oi, td);		}	}	return 0;}static int ohci_interrupt(void *arg){	ohci *oi = (ohci *)arg;	uint32 int_stat = oi->regs->interrupt_status;	int ret = INT_NO_RESCHEDULE;	int i;	if((int_stat & oi->regs->interrupt_enable) == 0)		return INT_NO_RESCHEDULE;	SHOW_FLOW(7, "oi %p int 0x%x (%d) enabled 0x%x disabled 0x%x", oi, int_stat, oi->rh_ports, oi->regs->interrupt_enable, oi->regs->interrupt_disable);	if(int_stat & INT_SO) {		SHOW_FLOW0(8, "\tscheduler overrun");	}	if(int_stat & INT_WDH) {		SHOW_FLOW0(8, "\twriteback done head");		int_stat &= ~INT_WDH; // dont ack it here		sem_release_etc(oi->done_list_sem, 1, SEM_FLAG_NO_RESCHED);		ret = INT_RESCHEDULE;	}	if(int_stat & INT_SOF) {		SHOW_FLOW0(8, "\tstart of frame");	}	if(int_stat & INT_RD) {		SHOW_FLOW0(8, "\tresume detected");	}	if(int_stat & INT_UE) {		SHOW_FLOW0(8, "\tunrecoverable error");	}	if(int_stat & INT_FNO) {		SHOW_FLOW0(8, "\tframe number overrun");	}	if(int_stat & INT_RHSC) {		SHOW_FLOW0(8, "\troot status change");		SHOW_FLOW(8, "\trh_status 0x%x", oi->regs->rh_status);		for(i = 0; i < oi->rh_ports; i++)			SHOW_FLOW(8, "\t\trh_port_status %d: 0x%x", i, oi->regs->rh_port_status[i]);	}	if(int_stat & INT_OC) {		SHOW_FLOW0(8, "\townership change");	}	if(int_stat & INT_MIE) {		SHOW_FLOW0(8, "\tmaster interrupt enable");	}	oi->regs->interrupt_status = int_stat;	return ret;}static int ohci_create_endpoint(hc_cookie *cookie, hc_endpoint **hc_endpoint,			usb_endpoint_descriptor *usb_endpoint, int address, bool lowspeed){	ohci *oi = (ohci *)cookie;	*hc_endpoint = create_endpoint(oi, usb_endpoint, address, lowspeed);	if(*hc_endpoint == NULL)		return ERR_NO_MEMORY;	return NO_ERROR;}static int ohci_destroy_endpoint(hc_cookie *cookie, hc_endpoint *hc_endpoint){	// XXX implement	return NO_ERROR;}static int ohci_enqueue_transfer(hc_cookie *cookie, hc_endpoint *endpoint, usb_hc_transfer *transfer){	ohci *oi = (ohci *)cookie;	ohci_ed *ed = (ohci_ed *)endpoint;	usb_endpoint_descriptor *usb_ed = ed->usb_ed;	switch(usb_ed->attributes & 0x3) {		case USB_ENDPOINT_ATTR_CONTROL: {			ohci_td *td0, *td1, *td2, *td3;			int dir;			// the direction of the transfer is based off of the first byte in the setup data			dir = *((uint8 *)transfer->setup_buf) & 0x80 ? 1 : 0;			mutex_lock(&oi->hc_list_lock);			SHOW_FLOW(6, "head 0x%x, tail 0x%x", ed->head, ed->tail);			// allocate the transfer descriptors needed			td0 = ed->tail_td;			td2 = allocate_td(oi);			if(transfer->data_buf != NULL)				td1 = allocate_td(oi);			else				td1 = td2;			td3 = allocate_td(oi); // this will be the new null descriptor			// setup descriptor			td0->flags = OHCI_TD_FLAGS_CC_NOT | OHCI_TD_FLAGS_DIR_SETUP | OHCI_TD_FLAGS_IRQ_NONE | OHCI_TD_FLAGS_TOGGLE_0;			vm_get_page_mapping(vm_get_kernel_aspace_id(), (addr_t)transfer->setup_buf, (addr_t *)&td0->curr_buf_ptr);			td0->buf_end = td0->curr_buf_ptr + transfer->setup_len - 1;			td0->next_td = td1->phys_addr; // might be td2			td0->transfer_head = td0;			td0->transfer_next = td1;			td0->usb_transfer = transfer;			td0->ed = ed;			td0->last_in_transfer = false;			SHOW_FLOW(6, "td0 %p (0x%lx) flags 0x%x, curr_buf_ptr 0x%x, buf_end 0x%x, next_td 0x%x",				td0, td0->phys_addr, td0->flags, td0->curr_buf_ptr, td0->buf_end, td0->next_td);			if(transfer->data_buf != NULL) {				// data descriptor				td1->flags = OHCI_TD_FLAGS_CC_NOT | OHCI_TD_FLAGS_TOGGLE_1| OHCI_TD_FLAGS_IRQ_NONE | OHCI_TD_FLAGS_ROUNDING |					(dir ? OHCI_TD_FLAGS_DIR_IN : OHCI_TD_FLAGS_DIR_OUT);				vm_get_page_mapping(vm_get_kernel_aspace_id(), (addr_t)transfer->data_buf, (addr_t *)&td1->curr_buf_ptr);				td1->buf_end = td1->curr_buf_ptr + transfer->data_len - 1;				td1->next_td = td2->phys_addr;				td1->transfer_head = td0;				td1->transfer_next = td2;				td1->usb_transfer = transfer;				td1->ed = ed;				td1->last_in_transfer = false;				SHOW_FLOW(6, "td1 %p (0x%lx) flags 0x%x, curr_buf_ptr 0x%x, buf_end 0x%x, next_td 0x%x",					td1, td1->phys_addr, td1->flags, td1->curr_buf_ptr, td1->buf_end, td1->next_td);			}			// ack descriptor			td2->flags = OHCI_TD_FLAGS_CC_NOT | OHCI_TD_FLAGS_TOGGLE_1 |				(dir ? OHCI_TD_FLAGS_DIR_OUT : OHCI_TD_FLAGS_DIR_IN);			td2->curr_buf_ptr = 0;			td2->buf_end = 0;			td2->next_td = td3->phys_addr;			td2->transfer_head = td0;			td2->transfer_next = NULL;			td2->usb_transfer = transfer;			td2->ed = ed;			td2->last_in_transfer = true;			SHOW_FLOW(6, "td2 %p (0x%lx) flags 0x%x, curr_buf_ptr 0x%x, buf_end 0x%x, next_td 0x%x",				td2, td2->phys_addr, td2->flags, td2->curr_buf_ptr, td2->buf_end, td2->next_td);			// next null descriptor is td3, it should be nulled out from allocate_td()			td3->ed = NULL;			td3->usb_transfer = NULL;			td3->transfer_head = td3->transfer_next = NULL;			SHOW_FLOW(6, "td3 %p (0x%lx) flags 0x%x, curr_buf_ptr 0x%x, buf_end 0x%x, next_td 0x%x",				td3, td3->phys_addr, td3->flags, td3->curr_buf_ptr, td3->buf_end, td3->next_td);			ed->tail_td = td3;			ed->tail = td3->phys_addr;			oi->regs->command_status = COMMAND_CLF;			mutex_unlock(&oi->hc_list_lock);			break;		}		case USB_ENDPOINT_ATTR_INT: {			ohci_td *td0, *td1;			mutex_lock(&oi->hc_list_lock);			td0 = ed->tail_td;			td1 = allocate_td(oi);			// XXX only deals with non page boundary data transfers			td0->flags = OHCI_TD_FLAGS_CC_NOT | OHCI_TD_FLAGS_ROUNDING;			vm_get_page_mapping(vm_get_kernel_aspace_id(), (addr_t)transfer->data_buf, (addr_t *)&td0->curr_buf_ptr);			td0->buf_end = td0->curr_buf_ptr + transfer->data_len - 1;			td0->next_td = td1->phys_addr;			td0->transfer_head = td0;			td0->transfer_next = td1;			td0->usb_transfer = transfer;			td0->ed = ed;			td0->last_in_transfer = true;			SHOW_FLOW(6, "td0 %p (0x%lx) flags 0x%x, curr_buf_ptr 0x%x, buf_end 0x%x, next_td 0x%x",

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -