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

📄 hc_sl811.c

📁 linux下面的SL811驱动硬件平台S3C44B0
💻 C
📖 第 1 页 / 共 5 页
字号:
/*		if ((td->ctrl & SL811_USB_CTRL_TOGGLE_1) != (td->status & SL811_USB_STS_TOGGLE_1)) {
			PDEBUG(2, "dev %d endpoint %d unexpect data toggle!", dev, ep);
			td->td_status = -EILSEQ;
		}
*/		
		if (!(td->ctrl & SL811_USB_CTRL_DIR_OUT) && td->len > 0)
			sl811_read_buf(hc, td->addr, td->buf, td->len - td->left);
			
		if (td->left && (urb->transfer_flags & USB_DISABLE_SPD)) {
			PDEBUG(2, "dev %d endpoint %d unexpect short packet! td = %p", dev, ep, td);
			td->td_status = -EREMOTEIO;
		} else
			td->td_status = 0;
	} else if (td->status & SL811_USB_STS_STALL) {
		PDEBUG(2, "dev %d endpoint %d halt, td = %p", dev, ep, td);
		td->td_status = -EPIPE;
		if (urb->dev)
			usb_endpoint_halt(td->urb->dev, usb_pipeendpoint(td->urb->pipe), usb_pipeout(td->urb->pipe));
		td->done = 1;
	} else if (td->status & SL811_USB_STS_OVERFLOW) {
		PDEBUG(1, "dev %d endpoint %d overflow, sl811 only support packet less than %d", dev, ep, SL811_DATA_LIMIT);	
		td->td_status = -EOVERFLOW;
		td->done = 1;
	} else if (td->status & SL811_USB_STS_TIMEOUT ) {
		PDEBUG(2, "dev %d endpoint %d timeout, td = %p", dev, ep, td);	
		td->td_status = -ETIMEDOUT;
		if (--td->errcnt == 0)
			td->done = 1;
	} else if (td->status & SL811_USB_STS_ERROR) {
		PDEBUG(2, "dev %d endpoint %d error, td = %p", dev, ep, td);
		td->td_status = -EILSEQ;
		if (--td->errcnt == 0)
			td->done = 1;
	} else if (td->status & SL811_USB_STS_NAK) {
		++td->nakcnt;
		PDEBUG(3, "dev %d endpoint %d nak, td = %p, count = %d", dev, ep, td, td->nakcnt);
		td->td_status = -EINPROGRESS;
		if (!usb_pipeslow(td->urb->pipe) && td->nakcnt > 1024) {
			PDEBUG(2, "too many naks, td = %p, count = %d", td, td->nakcnt);
			td->td_status = -ETIMEDOUT;
			td->done = 1;
		} 
	} 
	
	sl811_print_td(4, td);
}

/*
 * This	function checks	the status of current urb.
 */
static int sl811_parse_cur_urb(struct urb *urb)
{
	struct sl811_urb_priv *urbp = urb->hcpriv;
	struct sl811_td *td = urbp->cur_td;
	struct list_head *tmp;
	
	sl811_print_td(5, td);
	
	// this td not done yet.
	if (!td->done)
		return 0;
	
	// the last ld, so the urb is done.
	if (td == urbp->last_td) {
		PDEBUG(4, "urb = %p is done success", td->urb);
		if (usb_pipeisoc(td->urb->pipe))
			PDEBUG(4, "ISO URB DONE, td = %p", td);
		return 1;
	}
	
	// iso transfer, we always advance to next td 
	if (usb_pipeisoc(td->urb->pipe)) {
		tmp = &td->td_list;
		tmp = tmp->next;
		urbp->cur_td = list_entry(tmp, struct sl811_td, td_list);
		PDEBUG(4, "ISO NEXT, td = %p", urbp->cur_td);	
		return 0;
	}
		
	// some error occur, so the urb is done.
	if (td->td_status) {
		PDEBUG(3, "urb = %p is done error, td status is = %d", td->urb, td->td_status);
		return 1;
	}
		
	// short packet.
	if (td->left) {
		if (usb_pipecontrol(td->urb->pipe)) {
			// control packet, we advance to the last td
			PDEBUG(3, "ctrl short packet, advance to last td");
			urbp->cur_td = urbp->last_td;
			return 0;
		} else {
			// interrut and bulk packet, urb is over.
			PDEBUG(3, "bulk or intr short packet, urb is over");
			return 1;
		}
	}

	// we advance to next td.	
	tmp = &td->td_list;
	tmp = tmp->next;
	urbp->cur_td = list_entry(tmp, struct sl811_td, td_list);
#ifdef SL811_DEBUG
	PDEBUG(4, "advance to the next td, urb = %p, td = %p", urb, urbp->cur_td);
	sl811_print_td(5, urbp->cur_td);
	if (td == urbp->cur_td)
		PDEBUG(1, "bug!!!");
#endif		
	return 0;
}

/*
 * Find the next td to transfer.
 */
static inline struct sl811_td* sl811_schedule_next_td(struct urb *urb, struct sl811_td *cur_td)
{
	struct sl811_urb_priv *urbp = urb->hcpriv;
	
	PDEBUG(4, "urb at %p, cur td at %p", urb, cur_td);
	
	// iso don't schedule the td in the same frame.
	if (usb_pipeisoc(cur_td->urb->pipe))
		return NULL;
	
	// cur td is not complete
	if (!cur_td->done)
		return NULL;	
	
	// here, urbp->cur_td is already the next td;
	return urbp->cur_td;
}

/*
 * Scan the list to find a active urb
 */
static inline struct urb* sl811_get_list_next_urb(struct sl811_hc *hc, struct list_head *next)
{
	struct urb *urb;
	int i;
	
	if (list_empty(next))
		return NULL;
	
	if (next == hc->cur_list)
		return NULL;
			
	for (i = 0; i < 4; ++i) 
		if (next == &hc->urb_list[i])
			return NULL;
			
	urb = list_entry(next, struct urb, urb_list);
	PDEBUG(4, "next urb in list is at %p", urb);
		
	return urb;
}

/*
 * Find the next td to transfer.
 */
static struct sl811_td* sl811_schedule_next_urb(struct sl811_hc *hc, struct list_head *next)
{
	struct urb *urb = NULL;
	int back_loop = 1;
	struct list_head *old_list = hc->cur_list;
		
	// try to get next urb in the same list.
	if (next) {
		urb = sl811_get_list_next_urb(hc, next);
		if (!urb)
			++hc->cur_list;
	}

	// try other list.
	if (!urb) {			
re_loop:
		// try all the list.
		while (hc->cur_list < &hc->urb_list[4]) { 
			if ((urb = sl811_get_list_next_urb(hc, hc->cur_list->next)))
				return ((struct sl811_urb_priv *)urb->hcpriv)->cur_td;
			++hc->cur_list;
		}
		// the last list is try 
		if (back_loop && (old_list >= &hc->ctrl_list)) {
			hc->cur_list = &hc->ctrl_list;
			back_loop = 0;
			goto re_loop;
		}
	}
	
	if (hc->cur_list > &hc->urb_list[3])
		hc->cur_list = &hc->ctrl_list;
			
	return NULL;
}

/*
 * This function process the transfer rusult.
 */
static void sl811_transfer_done(struct sl811_hc *hc, int sof) 
{
	struct sl811_td *cur_td = hc->cur_td, *next_td = NULL;
	struct urb *cur_urb = NULL;
       	struct list_head *next = NULL;
       	int done;
	
	PDEBUG(5, "enter");
	
	if (cur_td == NULL) {
		PDEBUG(1, "in done interrupt, but td is null, be already parsed?");
		return ;
	}

	cur_urb = cur_td->urb;
	hc->cur_td = NULL;
	next = &cur_urb->urb_list;
	next = next->next;
	
	spin_lock(&cur_urb->lock);	
	sl811_parse_cur_td(hc, cur_td);
	done = sl811_parse_cur_urb(cur_urb);
	spin_unlock(&cur_urb->lock);
	
	if (done) {
		list_del_init(&cur_urb->urb_list);
		cur_td = NULL;
		sl811_result_urb(cur_urb);	
	}

	if (sof)
		return ;
	
	if (!done) {
		next_td = sl811_schedule_next_td(cur_urb, cur_td);
		if (next_td && next_td != cur_td && (sl811_calc_bus_remainder(hc) > next_td->bustime)) {
			hc->cur_td = next_td;
			PDEBUG(5, "ADD TD");
			sl811_trans_cur_td(hc, next_td);
			return ;
		}
	}
	
	while (1) {
		next_td = sl811_schedule_next_urb(hc, next);
		if (!next_td)
			return;
		if (next_td == cur_td)
			return;
		next = &next_td->urb->urb_list;
		next = next->next;
		if (sl811_calc_bus_remainder(hc) > next_td->bustime) {
			hc->cur_td = next_td;
			PDEBUG(5, "ADD TD");
			sl811_trans_cur_td(hc, next_td);
			return ;
		}
	}
}

/*
 *
 */
static void inline sl811_dec_intr_interval(struct sl811_hc *hc)
{
	struct list_head *head, *tmp;
	struct urb *urb;
	struct sl811_urb_priv *urbp;
	
	if (list_empty(&hc->idle_intr_list))
		return ;
	
	head = &hc->idle_intr_list;
	tmp = head->next;
	
	while (tmp != head) {
		urb = list_entry(tmp, struct urb, urb_list);
		tmp = tmp->next;
		spin_lock(&urb->lock);
		urbp = urb->hcpriv;
		if (--urbp->interval == 0) {
			list_del(&urb->urb_list);
			list_add(&urb->urb_list, &hc->intr_list);
			PDEBUG(4, "intr urb active");
		}
		spin_unlock(&urb->lock);
	}
}

/*
 * The sof interrupt is happen.	
 */
static void sl811_start_sof(struct sl811_hc *hc)
{
	struct sl811_td *next_td;
#ifdef SL811_DEBUG
	static struct sl811_td *repeat_td = NULL;
	static repeat_cnt = 1;
#endif	
	if (++hc->frame_number > 1024)
		hc->frame_number = 0;
	
	if (hc->active_urbs == 0)
		return ;
	
	sl811_dec_intr_interval(hc);
	
	if (hc->cur_td) {
		if (sl811_read(hc, 0) & SL811_USB_CTRL_ARM) {
#ifdef SL811_DEBUG
			if (repeat_td == hc->cur_td) 
				++repeat_cnt;
			else {
				if (repeat_cnt >= 2)
					PDEBUG(2, "cur td = %p repeat %d", hc->cur_td, repeat_cnt);
				repeat_cnt = 1;
				repeat_td = hc->cur_td;
			}
#endif
			return ;
		} else {
			PDEBUG(2, "lost of interrupt in sof? do parse!");
			sl811_transfer_done(hc, 1);
			
			// let this frame idle	
			return;
		}
	}
	
	hc->cur_list = &hc->iso_list;
	
	if (hc->active_urbs == 0)
		return ;
	
	next_td = sl811_schedule_next_urb(hc, NULL);
	if (!next_td) {
#ifdef SL811_DEBUG
		if (list_empty(&hc->idle_intr_list))
			PDEBUG(2, "not schedule a td, why? urbs = %d", hc->active_urbs);
#endif
		return; 
	}
	if (sl811_calc_bus_remainder(hc) > next_td->bustime) {
		hc->cur_td = next_td;
		sl811_trans_cur_td(hc, next_td);
	} else
		PDEBUG(2, "bus time if not enough, why?");
}

/*
 * This	function resets	SL811HS	controller and detects the speed of
 * the connecting device
 *
 * Return: 0 = no device attached; 1 = USB device attached
 */
static int sl811_hc_reset(struct sl811_hc *hc)
{
	int status ;

	sl811_write(hc,	SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
	sl811_write(hc,	SL811_CTRL1, SL811_CTRL1_RESET);//复位端点一

	mdelay(20);
	
	// Disable hardware SOF generation.
	sl811_write(hc,	SL811_CTRL1, 0);
	mdelay(2);
	sl811_write(hc, SL811_INTRSTS, 0xff); 
	status = sl811_read(hc, SL811_INTRSTS);

	if (status & SL811_INTR_NOTPRESENT) {//0x40=0100 0000 没有设备
		// Device is not present
		PDEBUG(0, "Device not present");
		hc->rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE);
		hc->rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
		sl811_write(hc,	SL811_INTR, SL811_INTR_INSRMV/*0x20 = 0010 0000 */);
		return 0;
	}

	// Send SOF to address 0, endpoint 0.
	sl811_write(hc, SL811_LEN_B, 0);
	sl811_write(hc, SL811_PIDEP_B, PIDEP(USB_PID_SOF/*include/linux/usb.h:0xa5*/, 0)/*PIDEP(USB_PID_SOF, 0)=0x50*/);
	sl811_write(hc, SL811_DEV_B, 0x00);
	sl811_write (hc, SL811_SOFLOW, SL811_12M_HI);

	if (status & SL811_INTR_SPEED_FULL) {
		/* full	speed device connect directly to root hub */
		PDEBUG (0, "Full speed Device attached");
		
		sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET);
		mdelay(20);
		sl811_write(hc,	SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
		sl811_write(hc, SL811_CTRL1, SL811_CTRL1_SOF);//硬件产生sof信号

		/* start the SOF or EOP	*/
		sl811_write(hc, SL811_CTRL_B, SL811_USB_CTRL_ARM);//允许传送
		hc->rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION;
		hc->rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED;
		mdelay(2);
		sl811_write (hc, SL811_INTRSTS, 0xff);
	} else {
		/* slow	speed device connect directly to root-hub */
		PDEBUG(0, "Low speed Device attached");
		
		sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET);
		mdelay(20);
		sl811_write(hc,	SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI);
		sl811_write(hc, SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF);

		/* start the SOF or EOP	*/
		sl811_write(hc, SL811_CTRL_B, SL811_USB_CTRL_ARM);
		hc->rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED;
		mdelay(2);
		sl811_write(hc, SL811_INTRSTS, 0xff);
	}

	hc->rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
	sl811_write(hc,	SL811_INTR, SL811_INTR_INSRMV);
	
	return 1;
}

/*
 * Interrupt service routine.
 */
static void sl811_interrupt(int irq, void *__hc, struct pt_regs * r)
{
	__u8 status;
	struct sl811_hc *hc = __hc;
	status = sl811_read(hc, SL811_INTRSTS);
	if (status == 0)
	{
		return ;
	}
	sl811_write(hc,	SL811_INTRSTS, 0xff);
	if(status&SL811_INTR_INSRMV)
	{
		info("device insort/remove");
		if(wh_initsl811(hc))
		{
			info("found device!\n");
		}
		else
		{
			info("not device!\n");
		}
	}
	
	
	if (status & SL811_INTR_DONE_A) 
	{
		info("usb a interrupt occured!\n");
	}
	return ;
}

/*
 * This	function allocates all data structure and store	in the
 * private data	structure.
 *
 * Return value	 : data structure for the host controller
 */
static struct sl811_hc* __devinit sl811_alloc_hc(void)
{
	struct sl811_hc *hc;
	struct usb_bus *bus;
	int i;

	PDEBUG(5, "enter");

	hc = (struct sl811_hc *)kmalloc(sizeof(struct sl811_hc), GFP_KERNEL);
	if (!hc)
		return NULL;

	memset(hc, 0, sizeof(struct sl811_hc));

	hc->rh_status.wPortStatus = USB_PORT_STAT_POWER;
	hc->rh_status.wPortChange = 0;

	hc->active_urbs	= 0;
	INIT_LIST_HEAD(&hc->hc_hcd_list);
	list_add(&hc->hc_hcd_list, &sl811_hcd_list);
	
	init_waitqueue_head(&hc->waitq);
	
	for (i = 0; i < 6; ++i)
		INIT_LIST_HEAD(&hc->urb_list[i]);
	
	hc->cur_list = &hc->iso_list;

	bus = usb_alloc_bus(&sl811_device_operations);
	if (!bus) 
	{
		kfree (hc);
		return NULL;
	}

	bus->bus_name = "sl811";
	hc->bus	= bus;
	bus->hcpriv = hc;

	return hc;
}

/*
 * This	function De-allocate all resources
 */
static void sl811_release_hc(struct sl811_hc *hc)
{
	PDEBUG(5, "enter");

	/* disconnect all devices */
	if (hc->bus->root_hub)
		usb_disconnect(&hc->bus->root_hub);

	if (hc->addr_io)
		release_region(hc->addr_io, 1);

	if (hc->data_io)
		release_region(hc->data_io, 1);

	if (hc->irq)
		free_irq(hc->irq, hc);

	usb_deregister_bus(hc->bus);
	usb_free_bus(hc->bus);

	list_del(&hc->hc_hcd_list);
	INIT_LIST_HEAD(&hc->hc_hcd_list);

	kfree (hc);
}

/*
 * This	function is board specific.  It	sets up	the in

⌨️ 快捷键说明

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