diff options
author | wdenk <wdenk> | 2004-04-23 20:32:05 +0000 |
---|---|---|
committer | wdenk <wdenk> | 2004-04-23 20:32:05 +0000 |
commit | 5cf91d6bdc3e60bd43f9ba1bbb97a43ee49b2b2d (patch) | |
tree | 5fe88f0a967da1a8d4f073b27294be5fa939c19d /drivers | |
parent | e35745bb64fae64da3e1fa7f4afe3213287f5908 (diff) | |
download | u-boot-imx-5cf91d6bdc3e60bd43f9ba1bbb97a43ee49b2b2d.zip u-boot-imx-5cf91d6bdc3e60bd43f9ba1bbb97a43ee49b2b2d.tar.gz u-boot-imx-5cf91d6bdc3e60bd43f9ba1bbb97a43ee49b2b2d.tar.bz2 |
* Modify KUP4X board configuration to use SL811 driver for USB memory
sticks (including FAT / VFAT filesystem support)
* Add SL811 Host Controller Interface driver for USB
* Add CFG_I2C_EEPROM_ADDR_OVERFLOW desription to README
* Patch by Pantelis Antoniou, 19 Apr 2004:
Allow to use shell style syntax (i. e. ${var} ) with standard parser.
Minor patches for Intracom boards.
* Patch by Christian Pell, 19 Apr 2004:
cleanup support for CF/IDE on PCMCIA for PXA25X
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/Makefile | 2 | ||||
-rw-r--r-- | drivers/sl811.h | 104 | ||||
-rw-r--r-- | drivers/sl811_usb.c | 705 |
3 files changed, 810 insertions, 1 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index ee2d8b9..0d1860e 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -40,7 +40,7 @@ OBJS = 3c589.o 5701rls.o ali512x.o \ rtl8019.o rtl8139.o rtl8169.o \ s3c24x0_i2c.o sed13806.o sed156x.o \ serial.o serial_max3100.o serial_pl010.o serial_pl011.o \ - serial_xuartlite.o \ + serial_xuartlite.o sl811_usb.o \ smc91111.o smiLynxEM.o status_led.o sym53c8xx.o \ ti_pci1410a.o tigon3.o w83c553f.o omap1510_i2c.o \ usbdcore.o usbdcore_ep0.o usbdcore_omap1510.o usbtty.o \ diff --git a/drivers/sl811.h b/drivers/sl811.h new file mode 100644 index 0000000..c1f9f01 --- /dev/null +++ b/drivers/sl811.h @@ -0,0 +1,104 @@ +#ifndef __UBOOT_SL811_H +#define __UBOOT_SL811_H + +#undef SL811_DEBUG + +#ifdef SL811_DEBUG + #define PDEBUG(level, fmt, args...) \ + if (debug >= (level)) printf("[%s:%d] " fmt, \ + __PRETTY_FUNCTION__, __LINE__ , ## args) +#else + #define PDEBUG(level, fmt, args...) do {} while(0) +#endif + +/* Sl811 host control register */ +#define SL811_CTRL_A 0x00 +#define SL811_ADDR_A 0x01 +#define SL811_LEN_A 0x02 +#define SL811_STS_A 0x03 /* read */ +#define SL811_PIDEP_A 0x03 /* write */ +#define SL811_CNT_A 0x04 /* read */ +#define SL811_DEV_A 0x04 /* write */ +#define SL811_CTRL1 0x05 +#define SL811_INTR 0x06 +#define SL811_CTRL_B 0x08 +#define SL811_ADDR_B 0x09 +#define SL811_LEN_B 0x0A +#define SL811_STS_B 0x0B /* read */ +#define SL811_PIDEP_B 0x0B /* write */ +#define SL811_CNT_B 0x0C /* read */ +#define SL811_DEV_B 0x0C /* write */ +#define SL811_INTRSTS 0x0D /* write clears bitwise */ +#define SL811_HWREV 0x0E /* read */ +#define SL811_SOFLOW 0x0E /* write */ +#define SL811_SOFCNTDIV 0x0F /* read */ +#define SL811_CTRL2 0x0F /* write */ + +/* USB control register bits (addr 0x00 and addr 0x08) */ +#define SL811_USB_CTRL_ARM 0x01 +#define SL811_USB_CTRL_ENABLE 0x02 +#define SL811_USB_CTRL_DIR_OUT 0x04 +#define SL811_USB_CTRL_ISO 0x10 +#define SL811_USB_CTRL_SOF 0x20 +#define SL811_USB_CTRL_TOGGLE_1 0x40 +#define SL811_USB_CTRL_PREAMBLE 0x80 + +/* USB status register bits (addr 0x03 and addr 0x0B) */ +#define SL811_USB_STS_ACK 0x01 +#define SL811_USB_STS_ERROR 0x02 +#define SL811_USB_STS_TIMEOUT 0x04 +#define SL811_USB_STS_TOGGLE_1 0x08 +#define SL811_USB_STS_SETUP 0x10 +#define SL811_USB_STS_OVERFLOW 0x20 +#define SL811_USB_STS_NAK 0x40 +#define SL811_USB_STS_STALL 0x80 + +/* Control register 1 bits (addr 0x05) */ +#define SL811_CTRL1_SOF 0x01 +#define SL811_CTRL1_RESET 0x08 +#define SL811_CTRL1_JKSTATE 0x10 +#define SL811_CTRL1_SPEED_LOW 0x20 +#define SL811_CTRL1_SUSPEND 0x40 + +/* Interrut enable (addr 0x06) and interrupt status register bits (addr 0x0D) */ +#define SL811_INTR_DONE_A 0x01 +#define SL811_INTR_DONE_B 0x02 +#define SL811_INTR_SOF 0x10 +#define SL811_INTR_INSRMV 0x20 +#define SL811_INTR_DETECT 0x40 +#define SL811_INTR_NOTPRESENT 0x40 +#define SL811_INTR_SPEED_FULL 0x80 /* only in status reg */ + +/* HW rev and SOF lo register bits (addr 0x0E) */ +#define SL811_HWR_HWREV 0xF0 + +/* SOF counter and control reg 2 (addr 0x0F) */ +#define SL811_CTL2_SOFHI 0x3F +#define SL811_CTL2_DSWAP 0x40 +#define SL811_CTL2_HOST 0x80 + +/* Set up for 1-ms SOF time. */ +#define SL811_12M_LOW 0xE0 +#define SL811_12M_HI 0x2E + +#define SL811_DATA_START 0x10 +#define SL811_DATA_LIMIT 240 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 + + +#define PIDEP(pid, ep) (((pid) & 0x0f) << 4 | (ep)) + +#endif /* __UBOOT_SL811_H */ diff --git a/drivers/sl811_usb.c b/drivers/sl811_usb.c new file mode 100644 index 0000000..a18e54d --- /dev/null +++ b/drivers/sl811_usb.c @@ -0,0 +1,705 @@ +/* + * (C) Copyright 2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This code is based on linux driver for sl811hs chip, source at + * drivers/usb/host/sl811.c: + * + * 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> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#ifdef CONFIG_USB_SL811HS +#include <mpc8xx.h> +#include <usb.h> +#include "sl811.h" + +#include "../board/kup/common/kup.h" + +#ifdef __PPC__ +# define EIEIO __asm__ volatile ("eieio") +#else +# define EIEIO /* nothing */ +#endif + +#define SL811_ADR (0x50000000) +#define SL811_DAT (0x50000001) + +#define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay(1000);}) + +#ifdef SL811_DEBUG +static int debug = 9; +#endif + +static int root_hub_devnum = 0; +static struct usb_port_status rh_status = { 0 };/* root hub port status */ + +static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe, + void *data, int buf_len, struct devrequest *cmd); + +static void sl811_write (__u8 index, __u8 data) +{ + *(volatile unsigned char *) (SL811_ADR) = index; + EIEIO; + *(volatile unsigned char *) (SL811_DAT) = data; + EIEIO; +} + +static __u8 sl811_read (__u8 index) +{ + __u8 data; + + *(volatile unsigned char *) (SL811_ADR) = index; + EIEIO; + data = *(volatile unsigned char *) (SL811_DAT); + EIEIO; + return (data); +} + +/* + * Read consecutive bytes of data from the SL811H/SL11H buffer + */ +static void inline sl811_read_buf(__u8 offset, __u8 *buf, __u8 size) +{ + *(volatile unsigned char *) (SL811_ADR) = offset; + EIEIO; + while (size--) { + *buf++ = *(volatile unsigned char *) (SL811_DAT); + EIEIO; + } +} + +/* + * Write consecutive bytes of data to the SL811H/SL11H buffer + */ +static void inline sl811_write_buf(__u8 offset, __u8 *buf, __u8 size) +{ + *(volatile unsigned char *) (SL811_ADR) = offset; + EIEIO; + while (size--) { + *(volatile unsigned char *) (SL811_DAT) = *buf++; + EIEIO; + } +} + +int usb_init_kup4x (void) +{ + volatile immap_t *immap = (immap_t *) CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + int i; + unsigned char tmp; + + memctl = &immap->im_memctl; + memctl->memc_or7 = 0xFFFF8726; + memctl->memc_br7 = 0x50000401; /* start at 0x50000000 */ + /* BP 14 low = USB ON */ + immap->im_cpm.cp_pbdat &= ~(BP_USB_VCC); + /* PB 14 nomal port */ + immap->im_cpm.cp_pbpar &= ~(BP_USB_VCC); + /* output */ + immap->im_cpm.cp_pbdir |= (BP_USB_VCC); + + puts ("USB: "); + + for (i = 0x10; i < 0xff; i++) { + sl811_write(i, i); + tmp = (sl811_read(i)); + if (tmp != i) { + printf ("SL811 compare error index=0x%02x read=0x%02x\n", i, tmp); + return (-1); + } + } + printf ("SL811 ready\n"); + return (0); +} + +/* + * This function resets SL811HS controller and detects the speed of + * the connecting device + * + * Return: 0 = no device attached; 1 = USB device attached + */ +static int sl811_hc_reset(void) +{ + int status ; + + sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); + sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); + + mdelay(20); + + /* Disable hardware SOF generation, clear all irq status. */ + sl811_write(SL811_CTRL1, 0); + mdelay(2); + sl811_write(SL811_INTRSTS, 0xff); + status = sl811_read(SL811_INTRSTS); + + if (status & SL811_INTR_NOTPRESENT) { + /* Device is not present */ + PDEBUG(0, "Device not present\n"); + rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE); + rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; + sl811_write(SL811_INTR, SL811_INTR_INSRMV); + return 0; + } + + /* Send SOF to address 0, endpoint 0. */ + sl811_write(SL811_LEN_B, 0); + sl811_write(SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0)); + sl811_write(SL811_DEV_B, 0x00); + sl811_write(SL811_SOFLOW, SL811_12M_LOW); + + if (status & SL811_INTR_SPEED_FULL) { + /* full speed device connect directly to root hub */ + PDEBUG (0, "Full speed Device attached\n"); + + sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); + mdelay(20); + sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); + sl811_write(SL811_CTRL1, SL811_CTRL1_SOF); + + /* start the SOF or EOP */ + sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM); + rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION; + rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED; + mdelay(2); + sl811_write(SL811_INTRSTS, 0xff); + } else { + /* slow speed device connect directly to root-hub */ + PDEBUG(0, "Low speed Device attached\n"); + + sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); + mdelay(20); + sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI); + sl811_write(SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF); + + /* start the SOF or EOP */ + sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM); + rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED; + mdelay(2); + sl811_write(SL811_INTRSTS, 0xff); + } + + rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; + sl811_write(SL811_INTR, /*SL811_INTR_INSRMV*/SL811_INTR_DONE_A); + + return 1; +} + +int usb_lowlevel_init(void) +{ + root_hub_devnum = 0; + sl811_hc_reset(); + return 0; +} + +int usb_lowlevel_stop(void) +{ + sl811_hc_reset(); + return 0; +} + +int sl811_send_packet(int dir_to_host, int data1, __u8 *buffer, int len) +{ + __u8 ctrl = SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE; + __u16 status; + int err = 0; + + if (len > 239) + return -1; + + if (!dir_to_host) + ctrl |= SL811_USB_CTRL_DIR_OUT; + if (data1) + ctrl |= SL811_USB_CTRL_TOGGLE_1; + + sl811_write(SL811_ADDR_A, 0x10); + sl811_write(SL811_LEN_A, len); + if (!dir_to_host && len) + sl811_write_buf(0x10, buffer, len); + + while (err < 3) { + if (sl811_read(SL811_SOFCNTDIV)*64 < len * 8 * 2) + ctrl |= SL811_USB_CTRL_SOF; + else + ctrl &= ~SL811_USB_CTRL_SOF; + sl811_write(SL811_CTRL_A, ctrl); + while (!(sl811_read(SL811_INTRSTS) & SL811_INTR_DONE_A)) + ; /* do nothing */ + + sl811_write(SL811_INTRSTS, 0xff); + status = sl811_read(SL811_STS_A); + + if (status & SL811_USB_STS_ACK) { + int remainder = sl811_read(SL811_CNT_A); + if (remainder) { + PDEBUG(0, "usb transfer remainder = %d\n", remainder); + len -= remainder; + } + if (dir_to_host && len) + sl811_read_buf(0x10, buffer, len); + return len; + } + + if ((status & SL811_USB_STS_NAK) == SL811_USB_STS_NAK) + continue; + + PDEBUG(0, "usb transfer error %#x\n", (int)status); + err++; + } + + return -1; +} + +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len) +{ + int dir_out = usb_pipeout(pipe); + int ep = usb_pipeendpoint(pipe); + __u8* buf = (__u8*)buffer; + int max = usb_maxpacket(dev, pipe); + int done = 0; + + PDEBUG(7, "dev = %ld pipe = %ld buf = %p size = %d dir_out = %d\n", + usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out); + + dev->status = 0; + + sl811_write(SL811_DEV_A, usb_pipedevice(pipe)); + sl811_write(SL811_PIDEP_A, PIDEP(!dir_out ? USB_PID_IN : USB_PID_OUT, ep)); + while (done < len) { + int res = sl811_send_packet(!dir_out, usb_gettoggle(dev, ep, dir_out), + buf+done, + max > len - done ? len - done : max); + if (res < 0) { + dev->status = res; + return res; + } + + if (!dir_out && res < max) /* short packet */ + break; + + done += res; + usb_dotoggle(dev, ep, dir_out); + } + + dev->act_len = done; + + return 0; +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len,struct devrequest *setup) +{ + int done = 0; + int devnum = usb_pipedevice(pipe); + + dev->status = 0; + + if (devnum == root_hub_devnum) + return sl811_rh_submit_urb(dev, pipe, buffer, len, setup); + + PDEBUG(7, "dev = %d pipe = %ld buf = %p size = %d rt = %#x req = %#x\n", + devnum, usb_pipeendpoint(pipe), buffer, len, (int)setup->requesttype, + (int)setup->request); + + sl811_write(SL811_DEV_A, devnum); + sl811_write(SL811_PIDEP_A, PIDEP(USB_PID_SETUP, 0)); + /* setup phase */ + if (sl811_send_packet(0, 0, (__u8*)setup, sizeof(*setup)) == sizeof(*setup)) { + int dir_in = setup->requesttype & USB_DIR_IN; + __u8* buf = (__u8*)buffer; + int data1 = 1; + int max = usb_maxpacket(dev, pipe); + + /* data phase */ + sl811_write(SL811_PIDEP_A, + PIDEP(dir_in ? USB_PID_IN : USB_PID_OUT, 0)); + while (done < len) { + int res = sl811_send_packet(dir_in, data1, buf+done, + max > len - done ? len - done : max); + if (res < 0) + return res; + done += res; + + if (dir_in && res < max) /* short packet */ + break; + + data1 = !data1; + } + + /* status phase */ + sl811_write(SL811_PIDEP_A, + PIDEP(!dir_in ? USB_PID_IN : USB_PID_OUT, 0)); + if (sl811_send_packet(!dir_in, 1, 0, 0) < 0) { + PDEBUG(0, "status phase failed!\n"); + dev->status = -1; + } + } else { + PDEBUG(0, "setup phase failed!\n"); + dev->status = -1; + } + + dev->act_len = done; + + return done; +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, int interval) +{ + PDEBUG(7, "dev = %p pipe = %#lx buf = %p size = %d int = %d\n", dev, pipe, + buffer, len, interval); + return -1; +} + +/* + * 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 *** */ +}; + +/* + * helper routine for returning string descriptors in UTF-16LE + * input can actually be ISO-8859-1; ASCII is its 7-bit subset + */ +static int ascii2utf (char *s, u8 *utf, int utfmax) +{ + int retval; + + for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) { + *utf++ = *s++; + *utf++ = 0; + } + return retval; +} + +/* + * root_hub_string is used by each host controller's root hub code, + * so that they're identified consistently throughout the system. + */ +int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len) +{ + char buf [30]; + + /* assert (len > (2 * (sizeof (buf) + 1))); + assert (strlen (type) <= 8);*/ + + /* language ids */ + if (id == 0) { + *data++ = 4; *data++ = 3; /* 4 bytes data */ + *data++ = 0; *data++ = 0; /* some language id */ + return 4; + + /* serial number */ + } else if (id == 1) { + sprintf (buf, "%x", serial); + + /* product description */ + } else if (id == 2) { + sprintf (buf, "USB %s Root Hub", type); + + /* id 3 == vendor description */ + + /* unsupported IDs --> "stall" */ + } else + return 0; + + data [0] = 2 + ascii2utf (buf, data + 2, len - 2); + data [1] = 3; + return data [0]; +} + +/* 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 usb_device *usb_dev, unsigned long pipe, + void *data, int buf_len, struct devrequest *cmd) +{ + __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)) { + PDEBUG(0, "interrupt transfer unimplemented!\n"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = le16_to_cpu (cmd->value); + wIndex = le16_to_cpu (cmd->index); + wLength = le16_to_cpu (cmd->length); + + PDEBUG(5, "submit rh urb, req = %d(%x) val = %#x index = %#x len=%d\n", + bmRType_bReq, bmRType_bReq, wValue, wIndex, 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(rh_status.wPortChange<<16 | 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: + rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE; + OK(0); + + case USB_PORT_FEAT_SUSPEND: + rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND; + OK(0); + + case USB_PORT_FEAT_POWER: + rh_status.wPortStatus &= ~USB_PORT_STAT_POWER; + OK(0); + + case USB_PORT_FEAT_C_CONNECTION: + rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION; + OK(0); + + case USB_PORT_FEAT_C_ENABLE: + rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE; + OK(0); + + case USB_PORT_FEAT_C_SUSPEND: + rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND; + OK(0); + + case USB_PORT_FEAT_C_OVER_CURRENT: + rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT; + OK(0); + + case USB_PORT_FEAT_C_RESET: + 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: + rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND; + OK(0); + + case USB_PORT_FEAT_RESET: + rh_status.wPortStatus |= USB_PORT_STAT_RESET; + rh_status.wPortChange = 0; + rh_status.wPortChange |= USB_PORT_STAT_C_RESET; + rh_status.wPortStatus &= ~USB_PORT_STAT_RESET; + rh_status.wPortStatus |= USB_PORT_STAT_ENABLE; + OK(0); + + case USB_PORT_FEAT_POWER: + rh_status.wPortStatus |= USB_PORT_STAT_POWER; + OK(0); + + case USB_PORT_FEAT_ENABLE: + rh_status.wPortStatus |= USB_PORT_STAT_ENABLE; + OK(0); + } + break; + + case RH_SET_ADDRESS: + root_hub_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 = -32; + } + break; + + case RH_GET_DESCRIPTOR | USB_TYPE_CLASS: + len = sizeof(sl811_rh_hub_des); + bufp = sl811_rh_hub_des; + OK(len); + + case RH_GET_CONFIGURATION: + bufp[0] = 0x01; + OK(1); + + case RH_SET_CONFIGURATION: + OK(0); + + default: + PDEBUG(1, "unsupported root hub command\n"); + status = -32; + } + + len = min(len, buf_len); + if (data != bufp) + memcpy(data, bufp, len); + + PDEBUG(5, "len = %d, status = %d\n", len, status); + + usb_dev->status = status; + usb_dev->act_len = len; + + return status == 0 ? len : status; +} + +#endif /* CONFIG_USB_SL811HS */ |