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

📄 hc_sl811.c

📁 linux下面的SL811驱动硬件平台S3C44B0
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 * SL811 Host Controller Interface driver for USB.
 * Considering the difference in your board, u should edit the 
 * It was editted by chinesefox to fit the 44b0x 2004.12.28
 * sl811_addr_io = your usb host controller addres
 * sl811_data_io = your usb host controller data i/o address
 * sl811_irq     = your usb host controller irq.
 *
 * 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>
 *
 * It's now support hronous mode and more effective than hc_sl811.o
 *
 * To do:
 *	1.Modify the timeout part, it's some messy
 *	2.Use usb-a and usb-b set in Ping-Pong mode
 	
 * 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 <asm/arch/irq.h>
#include <linux/usb.h>

#include "hcd.h"
#include "hub.h"
#include "hc_sl811.h"

#define writeb outb
#define readb inb
#define DRIVER_VERSION "v0.28"
#define DRIVER_AUTHOR "Yin Aihua <yinah@couragetech.com.cn>"
#define DRIVER_DESC "Sl811 USB Host Controller Driver"

static int sl811_addr_io = 0x04000000;
static int sl811_data_io = 0x04000008;
static int sl811_irq = 25;

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 = 4;

MODULE_PARM(sl811_addr_io,"i");
MODULE_PARM_DESC(sl811_addr_io,"sl811 address io port 0xf100000e");
MODULE_PARM(sl811_data_io,"i");
MODULE_PARM_DESC(sl811_data_io,"sl811 data io port 0xf100000f");
MODULE_PARM(sl811_irq,"i");
MODULE_PARM_DESC(sl811_irq,"sl811 irq 44(default)");
MODULE_PARM(debug,"i");
MODULE_PARM_DESC(debug,"debug level");

static void sl811_rh_int_timer_do(unsigned long ptr);
static void sl811_transfer_done(struct sl811_hc *hc, int sof); 
///////////////////////////////////////////////////
/*
	wh write for test
*/
typedef struct{
	unsigned char bmRequest;
	unsigned char bRequest;
	unsigned char wValue1;
	unsigned char wValue2;
	unsigned char wIndex1;
	unsigned char wIndex2;
	unsigned char wLength1;
	unsigned char wLength2;
}WH_STACK;

WH_STACK wh_setupstack;
unsigned char wh_cfg[6];
unsigned char wh_data[8];
///////////////////////////////////////////////////
/*
 * Read	a byte of data from the	SL811H/SL11H
 */
static __u8 sl811_read(struct sl811_hc *hc,	__u8 offset)
{
	__u8 data;

	writeb(offset, hc->addr_io);
	wmb();
	data = readb(hc->data_io);
	rmb();
	return data;
}

/*
 * Write a byte	of data	to the SL811H/SL11H
 */
static void sl811_write(struct sl811_hc *hc, __u8 offset, __u8 data)
{
	writeb(offset, hc->addr_io);
	writeb(data, hc->data_io);
	wmb();
}

/*
 * Read	consecutive bytes of data from the SL811H/SL11H	buffer
 */
static void sl811_read_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size)
{
	writeb(offset, hc->addr_io);
	wmb();
	while (size--) {
		*buf++ = readb(hc->data_io);
		rmb();
	}
}

/*
 * Write consecutive bytes of data to the SL811H/SL11H buffer
 */
static void sl811_write_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size)
{
	writeb(offset, hc->addr_io);
	wmb();
	while (size--) {
		writeb(*buf, hc->data_io);
		wmb();
		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;
		}
	}

	/* restore the data */
	for (i = 0x10; i < 256;	i++)
		sl811_write(hc,	i, buf[i]);

	return result;
}

/*
 * Display all SL811HS register	values
 */
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));
}

/*
 * 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:

⌨️ 快捷键说明

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