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

📄 sl811.c

📁 在LINUX下驱动USB芯片SL811的程序源码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * SL811 Host Controller Interface driver for USB. * * Copyright (c) 2003/06, Courage Co., Ltd. * * Based on: *	1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap, * 	  Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, * 	  Adam Richter, Gregory P. Smith; * 	2.Original SL811 driver (hc_sl811.o) by Pei Liu <pbl@cypress.com> *	3.Rewrited as sl811.o by Yin Aihua <yinah:couragetech.com.cn> * * It's now support isochornous mode and more effective than hc_sl811.o * Support x86 architecture now. * * 19.09.2003 (05.06.2003) HNE * sl811_alloc_hc: Set "bus->bus_name" at init. * sl811_reg_test (hc_reset,regTest): *   Stop output at first failed pattern. * Down-Grade for Kernel 2.4.20 and from 2.4.22 * Splitt hardware depens into file sl811-x86.h and sl811-arm.h. * * 22.09.2003 HNE * sl811_found_hc: First patterntest, than interrupt enable. * Do nothing, if patterntest failed. Release io, if failed. * Stop Interrupts first, than remove handle. (Old blocked Shred IRQ) * Alternate IO-Base for second Controller (CF/USB1). * * 24.09.2003 HNE * Remove all arm specific source (moved into include/asm/sl811-hw.h). * * 03.10.2003 HNE * Low level only for port io into hardware-include. * * To do: *	1.Modify the timeout part, it's some messy *	2.Use usb-a and usb-b set in Ping-Pong mode *	o Floppy do not work. *	o driver crash, if io region can't register * 	o Only as module tested! Compiled in Version not tested! * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/smp_lock.h>#include <linux/list.h>#include <linux/ioport.h>#include <asm/io.h>#include <linux/irq.h>#include <linux/usb.h>#include "../hcd.h"#include "../hub.h"#include "sl811.h"//#include "hc_sl811.h"//#include "hc_simple.h"#define DRIVER_VERSION "v0.30"#define MODNAME "SL811"#define DRIVER_AUTHOR "Yin Aihua <yinah@couragetech.com.cn>, Henry Nestler <hne@ist1.de>"#define DRIVER_DESC "Sl811 USB Host Controller Alternate Driver"#define SL11H_HWREVREG		0x0E	/* read */static LIST_HEAD(sl811_hcd_list);/* * 0, normal prompt and information * 1, error should not occur in normal * 2, error maybe occur in normal * 3, useful and detail debug information * 4, function level enter and level inforamtion * 5, endless information will output because of timer function or interrupt */static int debug = 1;MODULE_PARM(debug,"i");MODULE_PARM_DESC(debug,"debug level");#include <asm/sl811-hw.h>	/* Include hardware and board depens */static void sl811_rh_int_timer_do(unsigned long ptr);static void sl811_transfer_done(struct sl811_hc *hc, int sof);/* * Read	a byte of data from the	SL811H/SL11H */static __u8 inline sl811_read(struct sl811_hc *hc, __u8 offset){	sl811_write_index (hc, offset);	return (sl811_read_data (hc));}/* * Write a byte	of data	to the SL811H/SL11H */static void inline sl811_write(struct sl811_hc *hc, __u8 offset, __u8 data){	sl811_write_index_data (hc, offset, data);}/* * Read	consecutive bytes of data from the SL811H/SL11H	buffer */static void inline sl811_read_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size){//	sl811_write_index (hc, offset);//printk("\n sl811 rdbuf:");        sl811_write_index (hc, offset);	while (size--) {//		*buf++ = sl811_read_data(hc);		*buf = sl811_read_data(hc);//printk("%2x ",*buf);		buf++;	}}/* * Write consecutive bytes of data to the SL811H/SL11H buffer */static void inline sl811_write_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size){//printk("\n sl811 wrbuf:");	sl811_write_index (hc, offset);	while (size--) {//		sl811_write_data (hc, *buf);//printk("%2x ",*buf);		sl811_write_data (hc, *buf);		buf++;	}}/* * This	routine	test the Read/Write functionality of SL811HS registers */static int sl811_reg_test(struct sl811_hc *hc){	int i, data, result = 0;	__u8 buf[256];	for (i = 0x10; i < 256;	i++) {		/* save	the original buffer */		buf[i] = sl811_read(hc,	i);		/* Write the new data to the buffer */		sl811_write(hc,	i, i);	}	/* compare the written data */	for (i = 0x10; i < 256;	i++) {		data = sl811_read(hc, i);		if (data != i) {			PDEBUG(1, "Pattern test failed!! value = 0x%x, s/b 0x%x", data, i);			result = -1;			/* If no Debug, show only first failed Address */			if (!debug)			    break;		}	}	/* restore the data */	for (i = 0x10; i < 256;	i++)		sl811_write(hc,	i, buf[i]);	return result;}/* * Display all SL811HS register	values */#if 0 /* unused (hne) */static void sl811_reg_show(struct sl811_hc *hc){	int i;	for (i = 0; i <	256; i++)		PDEBUG(4, "offset %d: 0x%x", i, sl811_read(hc, i));}#endif/* * This	function enables SL811HS interrupts */static void sl811_enable_interrupt(struct sl811_hc *hc){	PDEBUG(4, "enter");	sl811_write(hc, SL811_INTR, SL811_INTR_DONE_A | SL811_INTR_SOF | SL811_INTR_INSRMV);}/* * This	function disables SL811HS interrupts */static void sl811_disable_interrupt(struct sl811_hc *hc){	PDEBUG(4, "enter");	// Disable all other interrupt except for insert/remove.	sl811_write(hc,	SL811_INTR, SL811_INTR_INSRMV);}/* * SL811 Virtual Root Hub *//* Device descriptor */static __u8 sl811_rh_dev_des[] ={	0x12,       /*  __u8  bLength; */	0x01,       /*  __u8  bDescriptorType; Device */	0x10,	    /*  __u16 bcdUSB; v1.1 */	0x01,	0x09,	    /*  __u8  bDeviceClass; HUB_CLASSCODE */	0x00,	    /*  __u8  bDeviceSubClass; */	0x00,       /*  __u8  bDeviceProtocol; */	0x08,       /*  __u8  bMaxPacketSize0; 8 Bytes */	0x00,       /*  __u16 idVendor; */	0x00,	0x00,       /*  __u16 idProduct; */ 	0x00,	0x00,       /*  __u16 bcdDevice; */ 	0x00,	0x00,       /*  __u8  iManufacturer; */	0x02,       /*  __u8  iProduct; */	0x01,       /*  __u8  iSerialNumber; */	0x01        /*  __u8  bNumConfigurations; */};/* Configuration descriptor */static __u8 sl811_rh_config_des[] ={	0x09,       /*  __u8  bLength; */	0x02,       /*  __u8  bDescriptorType; Configuration */	0x19,       /*  __u16 wTotalLength; */	0x00,	0x01,       /*  __u8  bNumInterfaces; */	0x01,       /*  __u8  bConfigurationValue; */	0x00,       /*  __u8  iConfiguration; */	0x40,       /*  __u8  bmAttributes;                    Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup,                    4..0: resvd */	0x00,       /*  __u8  MaxPower; */	/* interface */	0x09,       /*  __u8  if_bLength; */	0x04,       /*  __u8  if_bDescriptorType; Interface */	0x00,       /*  __u8  if_bInterfaceNumber; */	0x00,       /*  __u8  if_bAlternateSetting; */	0x01,       /*  __u8  if_bNumEndpoints; */	0x09,       /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */	0x00,       /*  __u8  if_bInterfaceSubClass; */	0x00,       /*  __u8  if_bInterfaceProtocol; */	0x00,       /*  __u8  if_iInterface; */	/* endpoint */	0x07,       /*  __u8  ep_bLength; */	0x05,       /*  __u8  ep_bDescriptorType; Endpoint */	0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */ 	0x03,       /*  __u8  ep_bmAttributes; Interrupt */ 	0x08,       /*  __u16 ep_wMaxPacketSize; */ 	0x00,	0xff        /*  __u8  ep_bInterval; 255 ms */};/* root hub class descriptor*/static __u8 sl811_rh_hub_des[] ={	0x09,			/*  __u8  bLength; */	0x29,			/*  __u8  bDescriptorType; Hub-descriptor */	0x01,			/*  __u8  bNbrPorts; */	0x00,			/* __u16  wHubCharacteristics; */	0x00,	0x50,			/*  __u8  bPwrOn2pwrGood; 2ms */	0x00,			/*  __u8  bHubContrCurrent; 0 mA */	0xfc,			/*  __u8  DeviceRemovable; *** 7 Ports max *** */	0xff			/*  __u8  PortPwrCtrlMask; *** 7 ports max *** */};/* * This function examine the port change in the virtual root hub. HUB INTERRUPT ENDPOINT. */static int sl811_rh_send_irq(struct sl811_hc *hc, __u8 *rh_change, int rh_len){	__u8 data = 0;			PDEBUG(5, "enter");	/*	 * Right now, It is assume the power is good and no changes and only one port.	 */	if (hc->rh_status.wPortChange & (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE)) {  		data = 1<<1;		*(__u8 *)rh_change = data;		return 1;	} else		return 0;}/* * This function creates a timer that act as interrupt pipe in the virtual hub. * * Note:  The virtual root hub's interrupt pipe are polled by the timer *        every "interval" ms */static void sl811_rh_init_int_timer(struct urb * urb){	 struct sl811_hc *hc = urb->dev->bus->hcpriv;	 hc->rh.interval = urb->interval;	 init_timer(&hc->rh.rh_int_timer);	 hc->rh.rh_int_timer.function = sl811_rh_int_timer_do;	 hc->rh.rh_int_timer.data = (unsigned long)urb;	 hc->rh.rh_int_timer.expires = jiffies +		(HZ * (urb->interval < 30? 30: urb->interval)) / 1000;	 add_timer (&hc->rh.rh_int_timer);}/* * This function is called when the timer expires.  It gets the the port * change data and pass along to the upper protocol. */static void sl811_rh_int_timer_do(unsigned long ptr){	int len;	struct urb *urb = (struct urb *)ptr;	struct sl811_hc *hc = urb->dev->bus->hcpriv;	PDEBUG (5, "enter");	if(hc->rh.send) {		len = sl811_rh_send_irq(hc, urb->transfer_buffer,			urb->transfer_buffer_length);		if (len > 0) {			urb->actual_length = len;			if (urb->complete)				urb->complete(urb);		}	}#ifdef SL811_TIMEOUT	{	struct list_head *head, *tmp;	struct sl811_urb_priv *urbp;	struct urb *u;	int i;	static int timeout_count = 0;// check time out every second	if (++timeout_count > 4) {		int max_scan = hc->active_urbs;		timeout_count = 0;		for (i = 0; i < 6; ++i) {			head = &hc->urb_list[i];			tmp = head->next;			while (tmp != head && max_scan--) {				u = list_entry(tmp, struct urb, urb_list);				urbp = (struct sl811_urb_priv *)u->hcpriv;				tmp = tmp->next;				// Check if the URB timed out				if (u->timeout && time_after_eq(jiffies, urbp->inserttime + u->timeout)) {					PDEBUG(3, "urb = %p time out, we kill it", urb);					u->transfer_flags |= USB_TIMEOUT_KILLED;				}			}		}	}}#endif	// re-activate the timer	sl811_rh_init_int_timer(urb);}/* helper macro */#define OK(x)	len = (x); break/* * This function handles all USB request to the the virtual root hub */static int sl811_rh_submit_urb(struct urb *urb){	struct usb_device *usb_dev = urb->dev;	struct sl811_hc *hc = usb_dev->bus->hcpriv;	struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *)urb->setup_packet;	void *data = urb->transfer_buffer;	int buf_len = urb->transfer_buffer_length;	unsigned int pipe = urb->pipe;	__u8 data_buf[16];	__u8 *bufp = data_buf;	int len = 0;	int status = 0;		__u16 bmRType_bReq;	__u16 wValue;	__u16 wIndex;	__u16 wLength;	if (usb_pipeint(pipe)) {		hc->rh.urb =  urb;		hc->rh.send = 1;		hc->rh.interval = urb->interval;		sl811_rh_init_int_timer(urb);		urb->status = 0;		return 0;	}	bmRType_bReq  = cmd->bRequestType | (cmd->bRequest << 8);	wValue        = le16_to_cpu (cmd->wValue);	wIndex        = le16_to_cpu (cmd->wIndex);	wLength       = le16_to_cpu (cmd->wLength);	PDEBUG(5, "submit rh urb, req = %d(%x) len=%d", bmRType_bReq, bmRType_bReq, wLength);	/* Request Destination:		   without flags: Device,		   USB_RECIP_INTERFACE: interface,		   USB_RECIP_ENDPOINT: endpoint,		   USB_TYPE_CLASS means HUB here,		   USB_RECIP_OTHER | USB_TYPE_CLASS  almost ever means HUB_PORT here	*/	switch (bmRType_bReq) {	case RH_GET_STATUS:		*(__u16 *)bufp = cpu_to_le16(1);		OK(2);	case RH_GET_STATUS | USB_RECIP_INTERFACE:		*(__u16 *)bufp = cpu_to_le16(0);		OK(2);	case RH_GET_STATUS | USB_RECIP_ENDPOINT:		*(__u16 *)bufp = cpu_to_le16(0);		OK(2);	case RH_GET_STATUS | USB_TYPE_CLASS:		*(__u32 *)bufp = cpu_to_le32(0);		OK(4);	case RH_GET_STATUS | USB_RECIP_OTHER | USB_TYPE_CLASS:		*(__u32 *)bufp = cpu_to_le32(hc->rh_status.wPortChange<<16 | hc->rh_status.wPortStatus);		OK(4);	case RH_CLEAR_FEATURE | USB_RECIP_ENDPOINT:		switch (wValue)	{		case 1: 			OK(0);		}		break;	case RH_CLEAR_FEATURE | USB_TYPE_CLASS:		switch (wValue) {		case C_HUB_LOCAL_POWER:			OK(0);		case C_HUB_OVER_CURRENT:			OK(0);		}		break;	case RH_CLEAR_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:		switch (wValue) {		case USB_PORT_FEAT_ENABLE:			hc->rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE;			OK(0);		case USB_PORT_FEAT_SUSPEND:			hc->rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND;			OK(0);		case USB_PORT_FEAT_POWER:			hc->rh_status.wPortStatus &= ~USB_PORT_STAT_POWER;			OK(0);		case USB_PORT_FEAT_C_CONNECTION:			hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION;			OK(0);		case USB_PORT_FEAT_C_ENABLE:			hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE;			OK(0);		case USB_PORT_FEAT_C_SUSPEND:			hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND;			OK(0);		case USB_PORT_FEAT_C_OVER_CURRENT:			hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT;			OK(0);		case USB_PORT_FEAT_C_RESET:			hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_RESET;			OK(0);		}		break;	case RH_SET_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:		switch (wValue) {		case USB_PORT_FEAT_SUSPEND:			hc->rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND;			OK(0);		case USB_PORT_FEAT_RESET:			hc->rh_status.wPortStatus |= USB_PORT_STAT_RESET;			hc->rh_status.wPortChange = 0;			hc->rh_status.wPortChange |= USB_PORT_STAT_C_RESET;			hc->rh_status.wPortStatus &= ~USB_PORT_STAT_RESET;			hc->rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;			OK(0);		case USB_PORT_FEAT_POWER:			hc->rh_status.wPortStatus |= USB_PORT_STAT_POWER;			OK(0);		case USB_PORT_FEAT_ENABLE:			hc->rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;			OK(0);		}		break;	case RH_SET_ADDRESS:		hc->rh.devnum = wValue;		OK(0);	case RH_GET_DESCRIPTOR:		switch ((wValue & 0xff00) >> 8) {		case USB_DT_DEVICE:			len = sizeof(sl811_rh_dev_des);			bufp = sl811_rh_dev_des;			OK(len);		case USB_DT_CONFIG: 			len = sizeof(sl811_rh_config_des);			bufp = sl811_rh_config_des;			OK(len);		case USB_DT_STRING:			len = usb_root_hub_string(wValue & 0xff, (int)(long)0,	"SL811HS", data, wLength);			if (len > 0) {				bufp = data;				OK(len);			}				default:			status = -EPIPE;		}		break;

⌨️ 快捷键说明

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