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

📄 isp116x-hcd.c

📁 usb driver for 2.6.17
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * 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/module.h>#include <linux/delay.h>#include <linux/debugfs.h>#include <linux/seq_file.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/list.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>#include "../core/hcd.h"#include "isp116x.h"#define DRIVER_VERSION	"03 Nov 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;	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 = &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);		short_not_ok = 1;		spin_lock(&urb->lock);		/* 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();		}		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)) {		list_del_init(&ep->schedule);		return;	}	/* periodic deschedule */	DBG("deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch);	for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {		struct isp116x_ep *temp;		struct isp116x_ep **prev = &isp116x->periodic[i];		while (*prev && ((temp = *prev) != ep))			prev = &temp->next;		if (*prev)			*prev = ep->next;		isp116x->load[i] -= ep->load;	}	ep->branch = PERIODIC_SIZE;	isp116x_to_hcd(isp116x)->self.bandwidth_allocated -=	    ep->load / ep->period;	/* switch irq type? */	if (!--isp116x->periodic_count) {		isp116x->irqenb &= ~HCuPINT_SOF;		isp116x->irqenb |= HCuPINT_ATL;	}}/*  Scan transfer lists, schedule transfers, send data off  to chip. */static void start_atl_transfers(struct isp116x *isp116x){	struct isp116x_ep *last_ep = NULL, *ep;	struct urb *urb;	u16 load = 0;	int len, index, speed, byte_time;	if (atomic_read(&isp116x->atl_finishing))		return;	if (!HC_IS_RUNNING(isp116x_to_hcd(isp116x)->state))		return;	/* FIFO not empty? */	if (isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_FULL)		return;	isp116x->atl_active = NULL;	isp116x->atl_buflen = isp116x->atl_bufshrt = 0;	/* Schedule int transfers */	if (isp116x->periodic_count) {		isp116x->fmindex = index =		    (isp116x->fmindex + 1) & (PERIODIC_SIZE - 1);		if ((load = isp116x->load[index])) {			/* Bring all int transfers for this frame			   into the active queue */			isp116x->atl_active = last_ep =			    isp116x->periodic[index];			while (last_ep->next)				last_ep = (last_ep->active = last_ep->next);			last_ep->active = NULL;		}	}	/* Schedule control/bulk transfers */	list_for_each_entry(ep, &isp116x->async, schedule) {		urb = container_of(ep->hep->urb_list.next,				   struct urb, urb_list);		speed = urb->dev->speed;		byte_time = speed == USB_SPEED_LOW		    ? BYTE_TIME_LOWSPEED : BYTE_TIME_FULLSPEED;		if (ep->nextpid == USB_PID_SETUP) {			len = sizeof(struct usb_ctrlrequest);		} else if (ep->nextpid == USB_PID_ACK) {			len = 0;		} else {			/* Find current free length ... */			len = (MAX_LOAD_LIMIT - load) / byte_time;			/* ... then limit it to configured max size ... */			len = min(len, speed == USB_SPEED_LOW ?				  MAX_TRANSFER_SIZE_LOWSPEED :				  MAX_TRANSFER_SIZE_FULLSPEED);			/* ... and finally cut to the multiple of MaxPacketSize,			   or to the real length if there's enough room. */			if (len <			    (urb->transfer_buffer_length -			     urb->actual_length)) {				len -= len % ep->maxpacket;				if (!len)					continue;			} else				len = urb->transfer_buffer_length -				    urb->actual_length;			BUG_ON(len < 0);		}		load += len * byte_time;		if (load > MAX_LOAD_LIMIT)			break;		ep->active = NULL;		ep->length = len;		if (last_ep)			last_ep->active = ep;		else			isp116x->atl_active = ep;		last_ep = ep;	}	/* Avoid starving of endpoints */	if ((&isp116x->async)->next != (&isp116x->async)->prev)		list_move(&isp116x->async, (&isp116x->async)->next);	if (isp116x->atl_active) {		preproc_atl_queue(isp116x);		pack_fifo(isp116x);	}}/*  Finish the processed transfers*/static void finish_atl_transfers(struct isp116x *isp116x, struct pt_regs *regs){	struct isp116x_ep *ep;	struct urb *urb;	if (!isp116x->atl_active)		return;	/* Fifo not ready? */	if (!(isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_DONE))		return;

⌨️ 快捷键说明

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