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

📄 isp116x-hcd.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * ISP116x HCD (Host Controller Driver) for USB. * * Derived from the SL811 HCD, rewritten for ISP116x. * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee> * * Portions: * Copyright (C) 2004 Psion Teklogix (for NetBook PRO) * Copyright (C) 2004 David Brownell * * Periodic scheduling is based on Roman's OHCI code * Copyright (C) 1999 Roman Weissgaerber * *//* * The driver basically works. A number of people have used it with a range * of devices. * * The driver passes all usbtests 1-14. * * Suspending/resuming of root hub via sysfs works. Remote wakeup works too. * And suspending/resuming of platform device works too. Suspend/resume * via HCD operations vector is not implemented. * * Iso transfer support is not implemented. Adding this would include * implementing recovery from the failure to service the processed ITL * fifo ram in time, which will involve chip reset. * * TODO: + More testing of suspend/resume.*//*  ISP116x chips require certain delays between accesses to its  registers. The following timing options exist.  1. Configure your memory controller (the best)  2. Implement platform-specific delay function possibly  combined with configuring the memory controller; see  include/linux/usb-isp116x.h for more info. Some broken  memory controllers line LH7A400 SMC need this. Also,  uncomment for that to work the following  USE_PLATFORM_DELAY macro.  3. Use ndelay (easiest, poorest). For that, uncomment  the following USE_NDELAY macro.*/#define USE_PLATFORM_DELAY//#define USE_NDELAY//#define DEBUG//#define VERBOSE/* Transfer descriptors. See dump_ptd() for printout format  *///#define PTD_TRACE/* enqueuing/finishing log of urbs *///#define URB_TRACE#include <linux/config.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/usb.h>#include <linux/usb_isp116x.h>#include <linux/platform_device.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/byteorder.h>#ifndef DEBUG#	define	STUB_DEBUG_FILE#endif#include "../core/hcd.h"#include "isp116x.h"#define DRIVER_VERSION	"05 Aug 2005"#define DRIVER_DESC	"ISP116x USB Host Controller Driver"MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");static const char hcd_name[] = "isp116x-hcd";/*-----------------------------------------------------------------*//*  Write len bytes to fifo, pad till 32-bit boundary */static void write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len){	u8 *dp = (u8 *) buf;	u16 *dp2 = (u16 *) buf;	u16 w;	int quot = len % 4;	if ((unsigned long)dp2 & 1) {		/* not aligned */		for (; len > 1; len -= 2) {			w = *dp++;			w |= *dp++ << 8;			isp116x_raw_write_data16(isp116x, w);		}		if (len)			isp116x_write_data16(isp116x, (u16) * dp);	} else {		/* aligned */		for (; len > 1; len -= 2)			isp116x_raw_write_data16(isp116x, *dp2++);		if (len)			isp116x_write_data16(isp116x, 0xff & *((u8 *) dp2));	}	if (quot == 1 || quot == 2)		isp116x_raw_write_data16(isp116x, 0);}/*  Read len bytes from fifo and then read till 32-bit boundary. */static void read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len){	u8 *dp = (u8 *) buf;	u16 *dp2 = (u16 *) buf;	u16 w;	int quot = len % 4;	if ((unsigned long)dp2 & 1) {		/* not aligned */		for (; len > 1; len -= 2) {			w = isp116x_raw_read_data16(isp116x);			*dp++ = w & 0xff;			*dp++ = (w >> 8) & 0xff;		}		if (len)			*dp = 0xff & isp116x_read_data16(isp116x);	} else {		/* aligned */		for (; len > 1; len -= 2)			*dp2++ = isp116x_raw_read_data16(isp116x);		if (len)			*(u8 *) dp2 = 0xff & isp116x_read_data16(isp116x);	}	if (quot == 1 || quot == 2)		isp116x_raw_read_data16(isp116x);}/*  Write ptd's and data for scheduled transfers into  the fifo ram. Fifo must be empty and ready.*/static void pack_fifo(struct isp116x *isp116x){	struct isp116x_ep *ep;	struct ptd *ptd;	int buflen = isp116x->atl_last_dir == PTD_DIR_IN	    ? isp116x->atl_bufshrt : isp116x->atl_buflen;	int ptd_count = 0;	isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT);	isp116x_write_reg16(isp116x, HCXFERCTR, buflen);	isp116x_write_addr(isp116x, HCATLPORT | ISP116x_WRITE_OFFSET);	for (ep = isp116x->atl_active; ep; ep = ep->active) {		++ptd_count;		ptd = &ep->ptd;		dump_ptd(ptd);		dump_ptd_out_data(ptd, ep->data);		isp116x_write_data16(isp116x, ptd->count);		isp116x_write_data16(isp116x, ptd->mps);		isp116x_write_data16(isp116x, ptd->len);		isp116x_write_data16(isp116x, ptd->faddr);		buflen -= sizeof(struct ptd);		/* Skip writing data for last IN PTD */		if (ep->active || (isp116x->atl_last_dir != PTD_DIR_IN)) {			write_ptddata_to_fifo(isp116x, ep->data, ep->length);			buflen -= ALIGN(ep->length, 4);		}	}	BUG_ON(buflen);}/*  Read the processed ptd's and data from fifo ram back to  URBs' buffers. Fifo must be full and done*/static void unpack_fifo(struct isp116x *isp116x){	struct isp116x_ep *ep;	struct ptd *ptd;	int buflen = isp116x->atl_last_dir == PTD_DIR_IN	    ? isp116x->atl_buflen : isp116x->atl_bufshrt;	isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT);	isp116x_write_reg16(isp116x, HCXFERCTR, buflen);	isp116x_write_addr(isp116x, HCATLPORT);	for (ep = isp116x->atl_active; ep; ep = ep->active) {		ptd = &ep->ptd;		ptd->count = isp116x_read_data16(isp116x);		ptd->mps = isp116x_read_data16(isp116x);		ptd->len = isp116x_read_data16(isp116x);		ptd->faddr = isp116x_read_data16(isp116x);		buflen -= sizeof(struct ptd);		/* Skip reading data for last Setup or Out PTD */		if (ep->active || (isp116x->atl_last_dir == PTD_DIR_IN)) {			read_ptddata_from_fifo(isp116x, ep->data, ep->length);			buflen -= ALIGN(ep->length, 4);		}		dump_ptd(ptd);		dump_ptd_in_data(ptd, ep->data);	}	BUG_ON(buflen);}/*---------------------------------------------------------------*//*  Set up PTD's.*/static void preproc_atl_queue(struct isp116x *isp116x){	struct isp116x_ep *ep;	struct urb *urb;	struct ptd *ptd;	u16 len;	for (ep = isp116x->atl_active; ep; ep = ep->active) {		u16 toggle = 0, dir = PTD_DIR_SETUP;		BUG_ON(list_empty(&ep->hep->urb_list));		urb = container_of(ep->hep->urb_list.next,				   struct urb, urb_list);		ptd = &ep->ptd;		len = ep->length;		spin_lock(&urb->lock);		ep->data = (unsigned char *)urb->transfer_buffer		    + urb->actual_length;		switch (ep->nextpid) {		case USB_PID_IN:			toggle = usb_gettoggle(urb->dev, ep->epnum, 0);			dir = PTD_DIR_IN;			break;		case USB_PID_OUT:			toggle = usb_gettoggle(urb->dev, ep->epnum, 1);			dir = PTD_DIR_OUT;			break;		case USB_PID_SETUP:			len = sizeof(struct usb_ctrlrequest);			ep->data = urb->setup_packet;			break;		case USB_PID_ACK:			toggle = 1;			len = 0;			dir = (urb->transfer_buffer_length			       && usb_pipein(urb->pipe))			    ? PTD_DIR_OUT : PTD_DIR_IN;			break;		default:			ERR("%s %d: ep->nextpid %d\n", __func__, __LINE__,			    ep->nextpid);			BUG();		}		ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | PTD_TOGGLE(toggle);		ptd->mps = PTD_MPS(ep->maxpacket)		    | PTD_SPD(urb->dev->speed == USB_SPEED_LOW)		    | PTD_EP(ep->epnum);		ptd->len = PTD_LEN(len) | PTD_DIR(dir);		ptd->faddr = PTD_FA(usb_pipedevice(urb->pipe));		spin_unlock(&urb->lock);		if (!ep->active) {			ptd->mps |= PTD_LAST_MSK;			isp116x->atl_last_dir = dir;		}		isp116x->atl_bufshrt = sizeof(struct ptd) + isp116x->atl_buflen;		isp116x->atl_buflen = isp116x->atl_bufshrt + ALIGN(len, 4);	}}/*  Analyze transfer results, handle partial transfers and errors*/static void postproc_atl_queue(struct isp116x *isp116x){	struct isp116x_ep *ep;	struct urb *urb;	struct usb_device *udev;	struct ptd *ptd;	int short_not_ok;	u8 cc;	for (ep = isp116x->atl_active; ep; ep = ep->active) {		BUG_ON(list_empty(&ep->hep->urb_list));		urb =		    container_of(ep->hep->urb_list.next, struct urb, urb_list);		udev = urb->dev;		ptd = &ep->ptd;		cc = PTD_GET_CC(ptd);		spin_lock(&urb->lock);		short_not_ok = 1;		/* Data underrun is special. For allowed underrun		   we clear the error and continue as normal. For		   forbidden underrun we finish the DATA stage		   immediately while for control transfer,		   we do a STATUS stage. */		if (cc == TD_DATAUNDERRUN) {			if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) {				DBG("Allowed data underrun\n");				cc = TD_CC_NOERROR;				short_not_ok = 0;			} else {				ep->error_count = 1;				if (usb_pipecontrol(urb->pipe))					ep->nextpid = USB_PID_ACK;				else					usb_settoggle(udev, ep->epnum,						      ep->nextpid ==						      USB_PID_OUT,						      PTD_GET_TOGGLE(ptd));				urb->actual_length += PTD_GET_COUNT(ptd);				urb->status = cc_to_error[TD_DATAUNDERRUN];				spin_unlock(&urb->lock);				continue;			}		}		/* Keep underrun error through the STATUS stage */		if (urb->status == cc_to_error[TD_DATAUNDERRUN])			cc = TD_DATAUNDERRUN;		if (cc != TD_CC_NOERROR && cc != TD_NOTACCESSED		    && (++ep->error_count >= 3 || cc == TD_CC_STALL			|| cc == TD_DATAOVERRUN)) {			if (urb->status == -EINPROGRESS)				urb->status = cc_to_error[cc];			if (ep->nextpid == USB_PID_ACK)				ep->nextpid = 0;			spin_unlock(&urb->lock);			continue;		}		/* According to usb spec, zero-length Int transfer signals		   finishing of the urb. Hey, does this apply only		   for IN endpoints? */		if (usb_pipeint(urb->pipe) && !PTD_GET_LEN(ptd)) {			if (urb->status == -EINPROGRESS)				urb->status = 0;			spin_unlock(&urb->lock);			continue;		}		/* Relax after previously failed, but later succeeded		   or correctly NAK'ed retransmission attempt */		if (ep->error_count		    && (cc == TD_CC_NOERROR || cc == TD_NOTACCESSED))			ep->error_count = 0;		/* Take into account idiosyncracies of the isp116x chip		   regarding toggle bit for failed transfers */		if (ep->nextpid == USB_PID_OUT)			usb_settoggle(udev, ep->epnum, 1, PTD_GET_TOGGLE(ptd)				      ^ (ep->error_count > 0));		else if (ep->nextpid == USB_PID_IN)			usb_settoggle(udev, ep->epnum, 0, PTD_GET_TOGGLE(ptd)				      ^ (ep->error_count > 0));		switch (ep->nextpid) {		case USB_PID_IN:		case USB_PID_OUT:			urb->actual_length += PTD_GET_COUNT(ptd);			if (PTD_GET_ACTIVE(ptd)			    || (cc != TD_CC_NOERROR && cc < 0x0E))				break;			if (urb->transfer_buffer_length != urb->actual_length) {				if (short_not_ok)					break;			} else {				if (urb->transfer_flags & URB_ZERO_PACKET				    && ep->nextpid == USB_PID_OUT				    && !(PTD_GET_COUNT(ptd) % ep->maxpacket)) {					DBG("Zero packet requested\n");					break;				}			}			/* All data for this URB is transferred, let's finish */			if (usb_pipecontrol(urb->pipe))				ep->nextpid = USB_PID_ACK;			else if (urb->status == -EINPROGRESS)				urb->status = 0;			break;		case USB_PID_SETUP:			if (PTD_GET_ACTIVE(ptd)			    || (cc != TD_CC_NOERROR && cc < 0x0E))				break;			if (urb->transfer_buffer_length == urb->actual_length)				ep->nextpid = USB_PID_ACK;			else if (usb_pipeout(urb->pipe)) {				usb_settoggle(udev, 0, 1, 1);				ep->nextpid = USB_PID_OUT;			} else {				usb_settoggle(udev, 0, 0, 1);				ep->nextpid = USB_PID_IN;			}			break;		case USB_PID_ACK:			if (PTD_GET_ACTIVE(ptd)			    || (cc != TD_CC_NOERROR && cc < 0x0E))				break;			if (urb->status == -EINPROGRESS)				urb->status = 0;			ep->nextpid = 0;			break;		default:			BUG_ON(1);		}		spin_unlock(&urb->lock);	}}/*  Take done or failed requests out of schedule. Give back  processed urbs.*/static void finish_request(struct isp116x *isp116x, struct isp116x_ep *ep,			   struct urb *urb, struct pt_regs *regs)__releases(isp116x->lock) __acquires(isp116x->lock){	unsigned i;	urb->hcpriv = NULL;	ep->error_count = 0;	if (usb_pipecontrol(urb->pipe))		ep->nextpid = USB_PID_SETUP;	urb_dbg(urb, "Finish");	spin_unlock(&isp116x->lock);	usb_hcd_giveback_urb(isp116x_to_hcd(isp116x), urb, regs);	spin_lock(&isp116x->lock);	/* take idle endpoints out of the schedule */	if (!list_empty(&ep->hep->urb_list))		return;	/* async deschedule */	if (!list_empty(&ep->schedule)) {

⌨️ 快捷键说明

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