📄 hc_sl811.c
字号:
/*
* 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 + -