xref: /rk3399_rockchip-uboot/drivers/usb/host/sl811-hcd.c (revision c60795f41d37600b6ebd79ec99252ec2f5efecd4)
12731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
22731b9a8SJean-Christophe PLAGNIOL-VILLARD  * (C) Copyright 2004
32731b9a8SJean-Christophe PLAGNIOL-VILLARD  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
42731b9a8SJean-Christophe PLAGNIOL-VILLARD  *
52731b9a8SJean-Christophe PLAGNIOL-VILLARD  * This code is based on linux driver for sl811hs chip, source at
62731b9a8SJean-Christophe PLAGNIOL-VILLARD  * drivers/usb/host/sl811.c:
72731b9a8SJean-Christophe PLAGNIOL-VILLARD  *
82731b9a8SJean-Christophe PLAGNIOL-VILLARD  * SL811 Host Controller Interface driver for USB.
92731b9a8SJean-Christophe PLAGNIOL-VILLARD  *
102731b9a8SJean-Christophe PLAGNIOL-VILLARD  * Copyright (c) 2003/06, Courage Co., Ltd.
112731b9a8SJean-Christophe PLAGNIOL-VILLARD  *
122731b9a8SJean-Christophe PLAGNIOL-VILLARD  * Based on:
132731b9a8SJean-Christophe PLAGNIOL-VILLARD  *	1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap,
142731b9a8SJean-Christophe PLAGNIOL-VILLARD  *	  Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber,
152731b9a8SJean-Christophe PLAGNIOL-VILLARD  *	  Adam Richter, Gregory P. Smith;
162731b9a8SJean-Christophe PLAGNIOL-VILLARD  *	2.Original SL811 driver (hc_sl811.o) by Pei Liu <pbl@cypress.com>
172731b9a8SJean-Christophe PLAGNIOL-VILLARD  *	3.Rewrited as sl811.o by Yin Aihua <yinah:couragetech.com.cn>
182731b9a8SJean-Christophe PLAGNIOL-VILLARD  *
192731b9a8SJean-Christophe PLAGNIOL-VILLARD  * See file CREDITS for list of people who contributed to this
202731b9a8SJean-Christophe PLAGNIOL-VILLARD  * project.
212731b9a8SJean-Christophe PLAGNIOL-VILLARD  *
222731b9a8SJean-Christophe PLAGNIOL-VILLARD  * This program is free software; you can redistribute it and/or
232731b9a8SJean-Christophe PLAGNIOL-VILLARD  * modify it under the terms of the GNU General Public License as
242731b9a8SJean-Christophe PLAGNIOL-VILLARD  * published by the Free Software Foundation; either version 2 of
252731b9a8SJean-Christophe PLAGNIOL-VILLARD  * the License, or (at your option) any later version.
262731b9a8SJean-Christophe PLAGNIOL-VILLARD  *
272731b9a8SJean-Christophe PLAGNIOL-VILLARD  * This program is distributed in the hope that it will be useful,
282731b9a8SJean-Christophe PLAGNIOL-VILLARD  * but WITHOUT ANY WARRANTY; without even the implied warranty of
292731b9a8SJean-Christophe PLAGNIOL-VILLARD  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
302731b9a8SJean-Christophe PLAGNIOL-VILLARD  * GNU General Public License for more details.
312731b9a8SJean-Christophe PLAGNIOL-VILLARD  *
322731b9a8SJean-Christophe PLAGNIOL-VILLARD  * You should have received a copy of the GNU General Public License
332731b9a8SJean-Christophe PLAGNIOL-VILLARD  * along with this program; if not, write to the Free Software
342731b9a8SJean-Christophe PLAGNIOL-VILLARD  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
352731b9a8SJean-Christophe PLAGNIOL-VILLARD  * MA 02111-1307 USA
362731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
372731b9a8SJean-Christophe PLAGNIOL-VILLARD 
382731b9a8SJean-Christophe PLAGNIOL-VILLARD #include <common.h>
392731b9a8SJean-Christophe PLAGNIOL-VILLARD #include <mpc8xx.h>
402731b9a8SJean-Christophe PLAGNIOL-VILLARD #include <usb.h>
412731b9a8SJean-Christophe PLAGNIOL-VILLARD #include "sl811.h"
422731b9a8SJean-Christophe PLAGNIOL-VILLARD 
432731b9a8SJean-Christophe PLAGNIOL-VILLARD #include "../../../board/kup/common/kup.h"
442731b9a8SJean-Christophe PLAGNIOL-VILLARD 
452731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef __PPC__
462731b9a8SJean-Christophe PLAGNIOL-VILLARD # define EIEIO		__asm__ volatile ("eieio")
472731b9a8SJean-Christophe PLAGNIOL-VILLARD #else
482731b9a8SJean-Christophe PLAGNIOL-VILLARD # define EIEIO		/* nothing */
492731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif
502731b9a8SJean-Christophe PLAGNIOL-VILLARD 
512731b9a8SJean-Christophe PLAGNIOL-VILLARD #define	 SL811_ADR (0x50000000)
522731b9a8SJean-Christophe PLAGNIOL-VILLARD #define	 SL811_DAT (0x50000001)
532731b9a8SJean-Christophe PLAGNIOL-VILLARD 
542731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef SL811_DEBUG
552731b9a8SJean-Christophe PLAGNIOL-VILLARD static int debug = 9;
562731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif
572731b9a8SJean-Christophe PLAGNIOL-VILLARD 
582731b9a8SJean-Christophe PLAGNIOL-VILLARD static int root_hub_devnum = 0;
592731b9a8SJean-Christophe PLAGNIOL-VILLARD static struct usb_port_status rh_status = { 0 };/* root hub port status */
602731b9a8SJean-Christophe PLAGNIOL-VILLARD 
612731b9a8SJean-Christophe PLAGNIOL-VILLARD static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe,
622731b9a8SJean-Christophe PLAGNIOL-VILLARD 			       void *data, int buf_len, struct devrequest *cmd);
632731b9a8SJean-Christophe PLAGNIOL-VILLARD 
642731b9a8SJean-Christophe PLAGNIOL-VILLARD static void sl811_write (__u8 index, __u8 data)
652731b9a8SJean-Christophe PLAGNIOL-VILLARD {
662731b9a8SJean-Christophe PLAGNIOL-VILLARD 	*(volatile unsigned char *) (SL811_ADR) = index;
672731b9a8SJean-Christophe PLAGNIOL-VILLARD 	EIEIO;
682731b9a8SJean-Christophe PLAGNIOL-VILLARD 	*(volatile unsigned char *) (SL811_DAT) = data;
692731b9a8SJean-Christophe PLAGNIOL-VILLARD 	EIEIO;
702731b9a8SJean-Christophe PLAGNIOL-VILLARD }
712731b9a8SJean-Christophe PLAGNIOL-VILLARD 
722731b9a8SJean-Christophe PLAGNIOL-VILLARD static __u8 sl811_read (__u8 index)
732731b9a8SJean-Christophe PLAGNIOL-VILLARD {
742731b9a8SJean-Christophe PLAGNIOL-VILLARD 	__u8 data;
752731b9a8SJean-Christophe PLAGNIOL-VILLARD 
762731b9a8SJean-Christophe PLAGNIOL-VILLARD 	*(volatile unsigned char *) (SL811_ADR) = index;
772731b9a8SJean-Christophe PLAGNIOL-VILLARD 	EIEIO;
782731b9a8SJean-Christophe PLAGNIOL-VILLARD 	data = *(volatile unsigned char *) (SL811_DAT);
792731b9a8SJean-Christophe PLAGNIOL-VILLARD 	EIEIO;
802731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return (data);
812731b9a8SJean-Christophe PLAGNIOL-VILLARD }
822731b9a8SJean-Christophe PLAGNIOL-VILLARD 
832731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
842731b9a8SJean-Christophe PLAGNIOL-VILLARD  * Read consecutive bytes of data from the SL811H/SL11H buffer
852731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
862731b9a8SJean-Christophe PLAGNIOL-VILLARD static void inline sl811_read_buf(__u8 offset, __u8 *buf, __u8 size)
872731b9a8SJean-Christophe PLAGNIOL-VILLARD {
882731b9a8SJean-Christophe PLAGNIOL-VILLARD 	*(volatile unsigned char *) (SL811_ADR) = offset;
892731b9a8SJean-Christophe PLAGNIOL-VILLARD 	EIEIO;
902731b9a8SJean-Christophe PLAGNIOL-VILLARD 	while (size--) {
912731b9a8SJean-Christophe PLAGNIOL-VILLARD 		*buf++ = *(volatile unsigned char *) (SL811_DAT);
922731b9a8SJean-Christophe PLAGNIOL-VILLARD 		EIEIO;
932731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
942731b9a8SJean-Christophe PLAGNIOL-VILLARD }
952731b9a8SJean-Christophe PLAGNIOL-VILLARD 
962731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
972731b9a8SJean-Christophe PLAGNIOL-VILLARD  * Write consecutive bytes of data to the SL811H/SL11H buffer
982731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
992731b9a8SJean-Christophe PLAGNIOL-VILLARD static void inline sl811_write_buf(__u8 offset, __u8 *buf, __u8 size)
1002731b9a8SJean-Christophe PLAGNIOL-VILLARD {
1012731b9a8SJean-Christophe PLAGNIOL-VILLARD 	*(volatile unsigned char *) (SL811_ADR) = offset;
1022731b9a8SJean-Christophe PLAGNIOL-VILLARD 	EIEIO;
1032731b9a8SJean-Christophe PLAGNIOL-VILLARD 	while (size--) {
1042731b9a8SJean-Christophe PLAGNIOL-VILLARD 		*(volatile unsigned char *) (SL811_DAT) = *buf++;
1052731b9a8SJean-Christophe PLAGNIOL-VILLARD 		EIEIO;
1062731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
1072731b9a8SJean-Christophe PLAGNIOL-VILLARD }
1082731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1092731b9a8SJean-Christophe PLAGNIOL-VILLARD int usb_init_kup4x (void)
1102731b9a8SJean-Christophe PLAGNIOL-VILLARD {
1112731b9a8SJean-Christophe PLAGNIOL-VILLARD 	volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
1122731b9a8SJean-Christophe PLAGNIOL-VILLARD 	volatile memctl8xx_t *memctl = &immap->im_memctl;
1132731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int i;
1142731b9a8SJean-Christophe PLAGNIOL-VILLARD 	unsigned char tmp;
1152731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1162731b9a8SJean-Christophe PLAGNIOL-VILLARD 	memctl = &immap->im_memctl;
1172731b9a8SJean-Christophe PLAGNIOL-VILLARD 	memctl->memc_or7 = 0xFFFF8726;
1182731b9a8SJean-Christophe PLAGNIOL-VILLARD 	memctl->memc_br7 = 0x50000401;	/* start at 0x50000000 */
1192731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* BP 14 low = USB ON */
1202731b9a8SJean-Christophe PLAGNIOL-VILLARD 	immap->im_cpm.cp_pbdat &= ~(BP_USB_VCC);
1212731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* PB 14 nomal port */
1222731b9a8SJean-Christophe PLAGNIOL-VILLARD 	immap->im_cpm.cp_pbpar &= ~(BP_USB_VCC);
1232731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* output */
1242731b9a8SJean-Christophe PLAGNIOL-VILLARD 	immap->im_cpm.cp_pbdir |= (BP_USB_VCC);
1252731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1262731b9a8SJean-Christophe PLAGNIOL-VILLARD 	puts ("USB:   ");
1272731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1282731b9a8SJean-Christophe PLAGNIOL-VILLARD 	for (i = 0x10; i < 0xff; i++) {
1292731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(i, i);
1302731b9a8SJean-Christophe PLAGNIOL-VILLARD 		tmp = (sl811_read(i));
1312731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (tmp != i) {
1322731b9a8SJean-Christophe PLAGNIOL-VILLARD 			printf ("SL811 compare error index=0x%02x read=0x%02x\n", i, tmp);
1332731b9a8SJean-Christophe PLAGNIOL-VILLARD 			return (-1);
1342731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
1352731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
1362731b9a8SJean-Christophe PLAGNIOL-VILLARD 	printf ("SL811 ready\n");
1372731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return (0);
1382731b9a8SJean-Christophe PLAGNIOL-VILLARD }
1392731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1402731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
1412731b9a8SJean-Christophe PLAGNIOL-VILLARD  * This function resets SL811HS controller and detects the speed of
1422731b9a8SJean-Christophe PLAGNIOL-VILLARD  * the connecting device
1432731b9a8SJean-Christophe PLAGNIOL-VILLARD  *
1442731b9a8SJean-Christophe PLAGNIOL-VILLARD  * Return: 0 = no device attached; 1 = USB device attached
1452731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
1462731b9a8SJean-Christophe PLAGNIOL-VILLARD static int sl811_hc_reset(void)
1472731b9a8SJean-Christophe PLAGNIOL-VILLARD {
1482731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int status ;
1492731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1502731b9a8SJean-Christophe PLAGNIOL-VILLARD 	sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
1512731b9a8SJean-Christophe PLAGNIOL-VILLARD 	sl811_write(SL811_CTRL1, SL811_CTRL1_RESET);
1522731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1532731b9a8SJean-Christophe PLAGNIOL-VILLARD 	mdelay(20);
1542731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1552731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Disable hardware SOF generation, clear all irq status. */
1562731b9a8SJean-Christophe PLAGNIOL-VILLARD 	sl811_write(SL811_CTRL1, 0);
1572731b9a8SJean-Christophe PLAGNIOL-VILLARD 	mdelay(2);
1582731b9a8SJean-Christophe PLAGNIOL-VILLARD 	sl811_write(SL811_INTRSTS, 0xff);
1592731b9a8SJean-Christophe PLAGNIOL-VILLARD 	status = sl811_read(SL811_INTRSTS);
1602731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1612731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (status & SL811_INTR_NOTPRESENT) {
1622731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Device is not present */
1632731b9a8SJean-Christophe PLAGNIOL-VILLARD 		PDEBUG(0, "Device not present\n");
1642731b9a8SJean-Christophe PLAGNIOL-VILLARD 		rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE);
1652731b9a8SJean-Christophe PLAGNIOL-VILLARD 		rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
1662731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(SL811_INTR, SL811_INTR_INSRMV);
1672731b9a8SJean-Christophe PLAGNIOL-VILLARD 		return 0;
1682731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
1692731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1702731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Send SOF to address 0, endpoint 0. */
1712731b9a8SJean-Christophe PLAGNIOL-VILLARD 	sl811_write(SL811_LEN_B, 0);
1722731b9a8SJean-Christophe PLAGNIOL-VILLARD 	sl811_write(SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0));
1732731b9a8SJean-Christophe PLAGNIOL-VILLARD 	sl811_write(SL811_DEV_B, 0x00);
1742731b9a8SJean-Christophe PLAGNIOL-VILLARD 	sl811_write(SL811_SOFLOW, SL811_12M_LOW);
1752731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1762731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (status & SL811_INTR_SPEED_FULL) {
1772731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* full speed device connect directly to root hub */
1782731b9a8SJean-Christophe PLAGNIOL-VILLARD 		PDEBUG (0, "Full speed Device attached\n");
1792731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1802731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(SL811_CTRL1, SL811_CTRL1_RESET);
1812731b9a8SJean-Christophe PLAGNIOL-VILLARD 		mdelay(20);
1822731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
1832731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(SL811_CTRL1, SL811_CTRL1_SOF);
1842731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1852731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* start the SOF or EOP */
1862731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM);
1872731b9a8SJean-Christophe PLAGNIOL-VILLARD 		rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION;
1882731b9a8SJean-Christophe PLAGNIOL-VILLARD 		rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED;
1892731b9a8SJean-Christophe PLAGNIOL-VILLARD 		mdelay(2);
1902731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(SL811_INTRSTS, 0xff);
1912731b9a8SJean-Christophe PLAGNIOL-VILLARD 	} else {
1922731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* slow speed device connect directly to root-hub */
1932731b9a8SJean-Christophe PLAGNIOL-VILLARD 		PDEBUG(0, "Low speed Device attached\n");
1942731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1952731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(SL811_CTRL1, SL811_CTRL1_RESET);
1962731b9a8SJean-Christophe PLAGNIOL-VILLARD 		mdelay(20);
1972731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI);
1982731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF);
1992731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2002731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* start the SOF or EOP */
2012731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM);
2022731b9a8SJean-Christophe PLAGNIOL-VILLARD 		rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED;
2032731b9a8SJean-Christophe PLAGNIOL-VILLARD 		mdelay(2);
2042731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(SL811_INTRSTS, 0xff);
2052731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
2062731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2072731b9a8SJean-Christophe PLAGNIOL-VILLARD 	rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
2082731b9a8SJean-Christophe PLAGNIOL-VILLARD 	sl811_write(SL811_INTR, /*SL811_INTR_INSRMV*/SL811_INTR_DONE_A);
2092731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2102731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return 1;
2112731b9a8SJean-Christophe PLAGNIOL-VILLARD }
2122731b9a8SJean-Christophe PLAGNIOL-VILLARD 
213c7e3b2b5SLucas Stach int usb_lowlevel_init(int index, void **controller)
2142731b9a8SJean-Christophe PLAGNIOL-VILLARD {
2152731b9a8SJean-Christophe PLAGNIOL-VILLARD 	root_hub_devnum = 0;
2162731b9a8SJean-Christophe PLAGNIOL-VILLARD 	sl811_hc_reset();
2172731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return 0;
2182731b9a8SJean-Christophe PLAGNIOL-VILLARD }
2192731b9a8SJean-Christophe PLAGNIOL-VILLARD 
220c7e3b2b5SLucas Stach int usb_lowlevel_stop(int index)
2212731b9a8SJean-Christophe PLAGNIOL-VILLARD {
2222731b9a8SJean-Christophe PLAGNIOL-VILLARD 	sl811_hc_reset();
2232731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return 0;
2242731b9a8SJean-Christophe PLAGNIOL-VILLARD }
2252731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2262731b9a8SJean-Christophe PLAGNIOL-VILLARD static int calc_needed_buswidth(int bytes, int need_preamble)
2272731b9a8SJean-Christophe PLAGNIOL-VILLARD {
2282731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return !need_preamble ? bytes * 8 + 256 : 8 * 8 * bytes + 2048;
2292731b9a8SJean-Christophe PLAGNIOL-VILLARD }
2302731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2312731b9a8SJean-Christophe PLAGNIOL-VILLARD static int sl811_send_packet(struct usb_device *dev, unsigned long pipe, __u8 *buffer, int len)
2322731b9a8SJean-Christophe PLAGNIOL-VILLARD {
2332731b9a8SJean-Christophe PLAGNIOL-VILLARD 	__u8 ctrl = SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE;
2342731b9a8SJean-Christophe PLAGNIOL-VILLARD 	__u16 status = 0;
2352731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int err = 0, time_start = get_timer(0);
2362731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int need_preamble = !(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) &&
237*c60795f4SIlya Yanok 		(dev->speed == USB_SPEED_LOW);
2382731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2392731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (len > 239)
2402731b9a8SJean-Christophe PLAGNIOL-VILLARD 		return -1;
2412731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2422731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (usb_pipeout(pipe))
2432731b9a8SJean-Christophe PLAGNIOL-VILLARD 		ctrl |= SL811_USB_CTRL_DIR_OUT;
2442731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)))
2452731b9a8SJean-Christophe PLAGNIOL-VILLARD 		ctrl |= SL811_USB_CTRL_TOGGLE_1;
2462731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (need_preamble)
2472731b9a8SJean-Christophe PLAGNIOL-VILLARD 		ctrl |= SL811_USB_CTRL_PREAMBLE;
2482731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2492731b9a8SJean-Christophe PLAGNIOL-VILLARD 	sl811_write(SL811_INTRSTS, 0xff);
2502731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2512731b9a8SJean-Christophe PLAGNIOL-VILLARD 	while (err < 3) {
2522731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(SL811_ADDR_A, 0x10);
2532731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(SL811_LEN_A, len);
2542731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (usb_pipeout(pipe) && len)
2552731b9a8SJean-Christophe PLAGNIOL-VILLARD 			sl811_write_buf(0x10, buffer, len);
2562731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2572731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (!(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) &&
2582731b9a8SJean-Christophe PLAGNIOL-VILLARD 		    sl811_read(SL811_SOFCNTDIV)*64 < calc_needed_buswidth(len, need_preamble))
2592731b9a8SJean-Christophe PLAGNIOL-VILLARD 			ctrl |= SL811_USB_CTRL_SOF;
2602731b9a8SJean-Christophe PLAGNIOL-VILLARD 		else
2612731b9a8SJean-Christophe PLAGNIOL-VILLARD 			ctrl &= ~SL811_USB_CTRL_SOF;
2622731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2632731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(SL811_CTRL_A, ctrl);
2642731b9a8SJean-Christophe PLAGNIOL-VILLARD 		while (!(sl811_read(SL811_INTRSTS) & SL811_INTR_DONE_A)) {
2652731b9a8SJean-Christophe PLAGNIOL-VILLARD 			if (5*CONFIG_SYS_HZ < get_timer(time_start)) {
2662731b9a8SJean-Christophe PLAGNIOL-VILLARD 				printf("USB transmit timed out\n");
2672731b9a8SJean-Christophe PLAGNIOL-VILLARD 				return -USB_ST_CRC_ERR;
2682731b9a8SJean-Christophe PLAGNIOL-VILLARD 			}
2692731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
2702731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2712731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(SL811_INTRSTS, 0xff);
2722731b9a8SJean-Christophe PLAGNIOL-VILLARD 		status = sl811_read(SL811_STS_A);
2732731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2742731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (status & SL811_USB_STS_ACK) {
2752731b9a8SJean-Christophe PLAGNIOL-VILLARD 			int remainder = sl811_read(SL811_CNT_A);
2762731b9a8SJean-Christophe PLAGNIOL-VILLARD 			if (remainder) {
2772731b9a8SJean-Christophe PLAGNIOL-VILLARD 				PDEBUG(0, "usb transfer remainder = %d\n", remainder);
2782731b9a8SJean-Christophe PLAGNIOL-VILLARD 				len -= remainder;
2792731b9a8SJean-Christophe PLAGNIOL-VILLARD 			}
2802731b9a8SJean-Christophe PLAGNIOL-VILLARD 			if (usb_pipein(pipe) && len)
2812731b9a8SJean-Christophe PLAGNIOL-VILLARD 				sl811_read_buf(0x10, buffer, len);
2822731b9a8SJean-Christophe PLAGNIOL-VILLARD 			return len;
2832731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
2842731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2852731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if ((status & SL811_USB_STS_NAK) == SL811_USB_STS_NAK)
2862731b9a8SJean-Christophe PLAGNIOL-VILLARD 			continue;
2872731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2882731b9a8SJean-Christophe PLAGNIOL-VILLARD 		PDEBUG(0, "usb transfer error %#x\n", (int)status);
2892731b9a8SJean-Christophe PLAGNIOL-VILLARD 		err++;
2902731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
2912731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2922731b9a8SJean-Christophe PLAGNIOL-VILLARD 	err = 0;
2932731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2942731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (status & SL811_USB_STS_ERROR)
2952731b9a8SJean-Christophe PLAGNIOL-VILLARD 		err |= USB_ST_BUF_ERR;
2962731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (status & SL811_USB_STS_TIMEOUT)
2972731b9a8SJean-Christophe PLAGNIOL-VILLARD 		err |= USB_ST_CRC_ERR;
2982731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (status & SL811_USB_STS_STALL)
2992731b9a8SJean-Christophe PLAGNIOL-VILLARD 		err |= USB_ST_STALLED;
3002731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3012731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return -err;
3022731b9a8SJean-Christophe PLAGNIOL-VILLARD }
3032731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3042731b9a8SJean-Christophe PLAGNIOL-VILLARD int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
3052731b9a8SJean-Christophe PLAGNIOL-VILLARD 		    int len)
3062731b9a8SJean-Christophe PLAGNIOL-VILLARD {
3072731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int dir_out = usb_pipeout(pipe);
3082731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int ep = usb_pipeendpoint(pipe);
3092731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int max = usb_maxpacket(dev, pipe);
3102731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int done = 0;
3112731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3122731b9a8SJean-Christophe PLAGNIOL-VILLARD 	PDEBUG(7, "dev = %ld pipe = %ld buf = %p size = %d dir_out = %d\n",
3132731b9a8SJean-Christophe PLAGNIOL-VILLARD 	       usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out);
3142731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3152731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->status = 0;
3162731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3172731b9a8SJean-Christophe PLAGNIOL-VILLARD 	sl811_write(SL811_DEV_A, usb_pipedevice(pipe));
3182731b9a8SJean-Christophe PLAGNIOL-VILLARD 	sl811_write(SL811_PIDEP_A, PIDEP(!dir_out ? USB_PID_IN : USB_PID_OUT, ep));
3192731b9a8SJean-Christophe PLAGNIOL-VILLARD 	while (done < len) {
3202731b9a8SJean-Christophe PLAGNIOL-VILLARD 		int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done,
3212731b9a8SJean-Christophe PLAGNIOL-VILLARD 					    max > len - done ? len - done : max);
3222731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (res < 0) {
3232731b9a8SJean-Christophe PLAGNIOL-VILLARD 			dev->status = -res;
3242731b9a8SJean-Christophe PLAGNIOL-VILLARD 			return res;
3252731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
3262731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3272731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (!dir_out && res < max) /* short packet */
3282731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
3292731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3302731b9a8SJean-Christophe PLAGNIOL-VILLARD 		done += res;
3312731b9a8SJean-Christophe PLAGNIOL-VILLARD 		usb_dotoggle(dev, ep, dir_out);
3322731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
3332731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3342731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->act_len = done;
3352731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3362731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return 0;
3372731b9a8SJean-Christophe PLAGNIOL-VILLARD }
3382731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3392731b9a8SJean-Christophe PLAGNIOL-VILLARD int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
3402731b9a8SJean-Christophe PLAGNIOL-VILLARD 		       int len,struct devrequest *setup)
3412731b9a8SJean-Christophe PLAGNIOL-VILLARD {
3422731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int done = 0;
3432731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int devnum = usb_pipedevice(pipe);
3442731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int ep = usb_pipeendpoint(pipe);
3452731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3462731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->status = 0;
3472731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3482731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (devnum == root_hub_devnum)
3492731b9a8SJean-Christophe PLAGNIOL-VILLARD 		return sl811_rh_submit_urb(dev, pipe, buffer, len, setup);
3502731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3512731b9a8SJean-Christophe PLAGNIOL-VILLARD 	PDEBUG(7, "dev = %d pipe = %ld buf = %p size = %d rt = %#x req = %#x bus = %i\n",
3522731b9a8SJean-Christophe PLAGNIOL-VILLARD 	       devnum, ep, buffer, len, (int)setup->requesttype,
3532731b9a8SJean-Christophe PLAGNIOL-VILLARD 	       (int)setup->request, sl811_read(SL811_SOFCNTDIV)*64);
3542731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3552731b9a8SJean-Christophe PLAGNIOL-VILLARD 	sl811_write(SL811_DEV_A, devnum);
3562731b9a8SJean-Christophe PLAGNIOL-VILLARD 	sl811_write(SL811_PIDEP_A, PIDEP(USB_PID_SETUP, ep));
3572731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* setup phase */
3582731b9a8SJean-Christophe PLAGNIOL-VILLARD 	usb_settoggle(dev, ep, 1, 0);
3592731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (sl811_send_packet(dev, usb_sndctrlpipe(dev, ep),
3602731b9a8SJean-Christophe PLAGNIOL-VILLARD 			      (__u8*)setup, sizeof(*setup)) == sizeof(*setup)) {
3612731b9a8SJean-Christophe PLAGNIOL-VILLARD 		int dir_in = usb_pipein(pipe);
3622731b9a8SJean-Christophe PLAGNIOL-VILLARD 		int max = usb_maxpacket(dev, pipe);
3632731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3642731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* data phase */
3652731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(SL811_PIDEP_A,
3662731b9a8SJean-Christophe PLAGNIOL-VILLARD 			    PIDEP(dir_in ? USB_PID_IN : USB_PID_OUT, ep));
3672731b9a8SJean-Christophe PLAGNIOL-VILLARD 		usb_settoggle(dev, ep, usb_pipeout(pipe), 1);
3682731b9a8SJean-Christophe PLAGNIOL-VILLARD 		while (done < len) {
3692731b9a8SJean-Christophe PLAGNIOL-VILLARD 			int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done,
3702731b9a8SJean-Christophe PLAGNIOL-VILLARD 						    max > len - done ? len - done : max);
3712731b9a8SJean-Christophe PLAGNIOL-VILLARD 			if (res < 0) {
3722731b9a8SJean-Christophe PLAGNIOL-VILLARD 				PDEBUG(0, "status data failed!\n");
3732731b9a8SJean-Christophe PLAGNIOL-VILLARD 				dev->status = -res;
3742731b9a8SJean-Christophe PLAGNIOL-VILLARD 				return 0;
3752731b9a8SJean-Christophe PLAGNIOL-VILLARD 			}
3762731b9a8SJean-Christophe PLAGNIOL-VILLARD 			done += res;
3772731b9a8SJean-Christophe PLAGNIOL-VILLARD 			usb_dotoggle(dev, ep, usb_pipeout(pipe));
3782731b9a8SJean-Christophe PLAGNIOL-VILLARD 			if (dir_in && res < max) /* short packet */
3792731b9a8SJean-Christophe PLAGNIOL-VILLARD 				break;
3802731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
3812731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3822731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* status phase */
3832731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sl811_write(SL811_PIDEP_A,
3842731b9a8SJean-Christophe PLAGNIOL-VILLARD 			    PIDEP(!dir_in ? USB_PID_IN : USB_PID_OUT, ep));
3852731b9a8SJean-Christophe PLAGNIOL-VILLARD 		usb_settoggle(dev, ep, !usb_pipeout(pipe), 1);
3862731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (sl811_send_packet(dev,
3872731b9a8SJean-Christophe PLAGNIOL-VILLARD 				      !dir_in ? usb_rcvctrlpipe(dev, ep) :
3882731b9a8SJean-Christophe PLAGNIOL-VILLARD 				      usb_sndctrlpipe(dev, ep),
3892731b9a8SJean-Christophe PLAGNIOL-VILLARD 				      0, 0) < 0) {
3902731b9a8SJean-Christophe PLAGNIOL-VILLARD 			PDEBUG(0, "status phase failed!\n");
3912731b9a8SJean-Christophe PLAGNIOL-VILLARD 			dev->status = -1;
3922731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
3932731b9a8SJean-Christophe PLAGNIOL-VILLARD 	} else {
3942731b9a8SJean-Christophe PLAGNIOL-VILLARD 		PDEBUG(0, "setup phase failed!\n");
3952731b9a8SJean-Christophe PLAGNIOL-VILLARD 		dev->status = -1;
3962731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
3972731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3982731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->act_len = done;
3992731b9a8SJean-Christophe PLAGNIOL-VILLARD 
4002731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return done;
4012731b9a8SJean-Christophe PLAGNIOL-VILLARD }
4022731b9a8SJean-Christophe PLAGNIOL-VILLARD 
4032731b9a8SJean-Christophe PLAGNIOL-VILLARD int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
4042731b9a8SJean-Christophe PLAGNIOL-VILLARD 		   int len, int interval)
4052731b9a8SJean-Christophe PLAGNIOL-VILLARD {
4062731b9a8SJean-Christophe PLAGNIOL-VILLARD 	PDEBUG(0, "dev = %p pipe = %#lx buf = %p size = %d int = %d\n", dev, pipe,
4072731b9a8SJean-Christophe PLAGNIOL-VILLARD 	       buffer, len, interval);
4082731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return -1;
4092731b9a8SJean-Christophe PLAGNIOL-VILLARD }
4102731b9a8SJean-Christophe PLAGNIOL-VILLARD 
4112731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
4122731b9a8SJean-Christophe PLAGNIOL-VILLARD  * SL811 Virtual Root Hub
4132731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
4142731b9a8SJean-Christophe PLAGNIOL-VILLARD 
4152731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Device descriptor */
4162731b9a8SJean-Christophe PLAGNIOL-VILLARD static __u8 sl811_rh_dev_des[] =
4172731b9a8SJean-Christophe PLAGNIOL-VILLARD {
4182731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x12,	    /*	__u8  bLength; */
4192731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x01,	    /*	__u8  bDescriptorType; Device */
4202731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x10,	    /*	__u16 bcdUSB; v1.1 */
4212731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x01,
4222731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x09,	    /*	__u8  bDeviceClass; HUB_CLASSCODE */
4232731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,	    /*	__u8  bDeviceSubClass; */
4242731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,	    /*	__u8  bDeviceProtocol; */
4252731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x08,	    /*	__u8  bMaxPacketSize0; 8 Bytes */
4262731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,	    /*	__u16 idVendor; */
4272731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,
4282731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,	    /*	__u16 idProduct; */
4292731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,
4302731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,	    /*	__u16 bcdDevice; */
4312731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,
4322731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,	    /*	__u8  iManufacturer; */
4332731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x02,	    /*	__u8  iProduct; */
4342731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x01,	    /*	__u8  iSerialNumber; */
4352731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x01	    /*	__u8  bNumConfigurations; */
4362731b9a8SJean-Christophe PLAGNIOL-VILLARD };
4372731b9a8SJean-Christophe PLAGNIOL-VILLARD 
4382731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Configuration descriptor */
4392731b9a8SJean-Christophe PLAGNIOL-VILLARD static __u8 sl811_rh_config_des[] =
4402731b9a8SJean-Christophe PLAGNIOL-VILLARD {
4412731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x09,	    /*	__u8  bLength; */
4422731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x02,	    /*	__u8  bDescriptorType; Configuration */
4432731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x19,	    /*	__u16 wTotalLength; */
4442731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,
4452731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x01,	    /*	__u8  bNumInterfaces; */
4462731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x01,	    /*	__u8  bConfigurationValue; */
4472731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,	    /*	__u8  iConfiguration; */
4482731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x40,	    /*	__u8  bmAttributes;
4492731b9a8SJean-Christophe PLAGNIOL-VILLARD 		    Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup,
4502731b9a8SJean-Christophe PLAGNIOL-VILLARD 		    4..0: resvd */
4512731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,	    /*	__u8  MaxPower; */
4522731b9a8SJean-Christophe PLAGNIOL-VILLARD 
4532731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* interface */
4542731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x09,	    /*	__u8  if_bLength; */
4552731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x04,	    /*	__u8  if_bDescriptorType; Interface */
4562731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,	    /*	__u8  if_bInterfaceNumber; */
4572731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,	    /*	__u8  if_bAlternateSetting; */
4582731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x01,	    /*	__u8  if_bNumEndpoints; */
4592731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x09,	    /*	__u8  if_bInterfaceClass; HUB_CLASSCODE */
4602731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,	    /*	__u8  if_bInterfaceSubClass; */
4612731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,	    /*	__u8  if_bInterfaceProtocol; */
4622731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,	    /*	__u8  if_iInterface; */
4632731b9a8SJean-Christophe PLAGNIOL-VILLARD 
4642731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* endpoint */
4652731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x07,	    /*	__u8  ep_bLength; */
4662731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x05,	    /*	__u8  ep_bDescriptorType; Endpoint */
4672731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x81,	    /*	__u8  ep_bEndpointAddress; IN Endpoint 1 */
4682731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x03,	    /*	__u8  ep_bmAttributes; Interrupt */
4692731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x08,	    /*	__u16 ep_wMaxPacketSize; */
4702731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,
4712731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0xff	    /*	__u8  ep_bInterval; 255 ms */
4722731b9a8SJean-Christophe PLAGNIOL-VILLARD };
4732731b9a8SJean-Christophe PLAGNIOL-VILLARD 
4742731b9a8SJean-Christophe PLAGNIOL-VILLARD /* root hub class descriptor*/
4752731b9a8SJean-Christophe PLAGNIOL-VILLARD static __u8 sl811_rh_hub_des[] =
4762731b9a8SJean-Christophe PLAGNIOL-VILLARD {
4772731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x09,			/*  __u8  bLength; */
4782731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x29,			/*  __u8  bDescriptorType; Hub-descriptor */
4792731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x01,			/*  __u8  bNbrPorts; */
4802731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,			/* __u16  wHubCharacteristics; */
4812731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,
4822731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x50,			/*  __u8  bPwrOn2pwrGood; 2ms */
4832731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0x00,			/*  __u8  bHubContrCurrent; 0 mA */
4842731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0xfc,			/*  __u8  DeviceRemovable; *** 7 Ports max *** */
4852731b9a8SJean-Christophe PLAGNIOL-VILLARD 	0xff			/*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
4862731b9a8SJean-Christophe PLAGNIOL-VILLARD };
4872731b9a8SJean-Christophe PLAGNIOL-VILLARD 
4882731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
4892731b9a8SJean-Christophe PLAGNIOL-VILLARD  * helper routine for returning string descriptors in UTF-16LE
4902731b9a8SJean-Christophe PLAGNIOL-VILLARD  * input can actually be ISO-8859-1; ASCII is its 7-bit subset
4912731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
4922731b9a8SJean-Christophe PLAGNIOL-VILLARD static int ascii2utf (char *s, u8 *utf, int utfmax)
4932731b9a8SJean-Christophe PLAGNIOL-VILLARD {
4942731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int retval;
4952731b9a8SJean-Christophe PLAGNIOL-VILLARD 
4962731b9a8SJean-Christophe PLAGNIOL-VILLARD 	for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) {
4972731b9a8SJean-Christophe PLAGNIOL-VILLARD 		*utf++ = *s++;
4982731b9a8SJean-Christophe PLAGNIOL-VILLARD 		*utf++ = 0;
4992731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
5002731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return retval;
5012731b9a8SJean-Christophe PLAGNIOL-VILLARD }
5022731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5032731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
5042731b9a8SJean-Christophe PLAGNIOL-VILLARD  * root_hub_string is used by each host controller's root hub code,
5052731b9a8SJean-Christophe PLAGNIOL-VILLARD  * so that they're identified consistently throughout the system.
5062731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
5072731b9a8SJean-Christophe PLAGNIOL-VILLARD static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
5082731b9a8SJean-Christophe PLAGNIOL-VILLARD {
5092731b9a8SJean-Christophe PLAGNIOL-VILLARD 	char buf [30];
5102731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5112731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* assert (len > (2 * (sizeof (buf) + 1)));
5122731b9a8SJean-Christophe PLAGNIOL-VILLARD 	   assert (strlen (type) <= 8);*/
5132731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5142731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* language ids */
5152731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (id == 0) {
5162731b9a8SJean-Christophe PLAGNIOL-VILLARD 		*data++ = 4; *data++ = 3;	/* 4 bytes data */
5172731b9a8SJean-Christophe PLAGNIOL-VILLARD 		*data++ = 0; *data++ = 0;	/* some language id */
5182731b9a8SJean-Christophe PLAGNIOL-VILLARD 		return 4;
5192731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5202731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* serial number */
5212731b9a8SJean-Christophe PLAGNIOL-VILLARD 	} else if (id == 1) {
5222731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sprintf (buf, "%#x", serial);
5232731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5242731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* product description */
5252731b9a8SJean-Christophe PLAGNIOL-VILLARD 	} else if (id == 2) {
5262731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sprintf (buf, "USB %s Root Hub", type);
5272731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5282731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* id 3 == vendor description */
5292731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5302731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* unsupported IDs --> "stall" */
5312731b9a8SJean-Christophe PLAGNIOL-VILLARD 	} else
5322731b9a8SJean-Christophe PLAGNIOL-VILLARD 	    return 0;
5332731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5342731b9a8SJean-Christophe PLAGNIOL-VILLARD 	ascii2utf (buf, data + 2, len - 2);
5352731b9a8SJean-Christophe PLAGNIOL-VILLARD 	data [0] = 2 + strlen(buf) * 2;
5362731b9a8SJean-Christophe PLAGNIOL-VILLARD 	data [1] = 3;
5372731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return data [0];
5382731b9a8SJean-Christophe PLAGNIOL-VILLARD }
5392731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5402731b9a8SJean-Christophe PLAGNIOL-VILLARD /* helper macro */
5412731b9a8SJean-Christophe PLAGNIOL-VILLARD #define OK(x)	len = (x); break
5422731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5432731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
5442731b9a8SJean-Christophe PLAGNIOL-VILLARD  * This function handles all USB request to the the virtual root hub
5452731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
5462731b9a8SJean-Christophe PLAGNIOL-VILLARD static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe,
5472731b9a8SJean-Christophe PLAGNIOL-VILLARD 			       void *data, int buf_len, struct devrequest *cmd)
5482731b9a8SJean-Christophe PLAGNIOL-VILLARD {
5492731b9a8SJean-Christophe PLAGNIOL-VILLARD 	__u8 data_buf[16];
5502731b9a8SJean-Christophe PLAGNIOL-VILLARD 	__u8 *bufp = data_buf;
5512731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int len = 0;
5522731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int status = 0;
5532731b9a8SJean-Christophe PLAGNIOL-VILLARD 	__u16 bmRType_bReq;
554b0b20d47SWolfgang Denk 	__u16 wValue  = le16_to_cpu (cmd->value);
555b0b20d47SWolfgang Denk 	__u16 wLength = le16_to_cpu (cmd->length);
556b0b20d47SWolfgang Denk #ifdef SL811_DEBUG
557b0b20d47SWolfgang Denk 	__u16 wIndex  = le16_to_cpu (cmd->index);
558b0b20d47SWolfgang Denk #endif
5592731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5602731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (usb_pipeint(pipe)) {
5612731b9a8SJean-Christophe PLAGNIOL-VILLARD 		PDEBUG(0, "interrupt transfer unimplemented!\n");
5622731b9a8SJean-Christophe PLAGNIOL-VILLARD 		return 0;
5632731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
5642731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5652731b9a8SJean-Christophe PLAGNIOL-VILLARD 	bmRType_bReq  = cmd->requesttype | (cmd->request << 8);
5662731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5672731b9a8SJean-Christophe PLAGNIOL-VILLARD 	PDEBUG(5, "submit rh urb, req = %d(%x) val = %#x index = %#x len=%d\n",
5682731b9a8SJean-Christophe PLAGNIOL-VILLARD 	       bmRType_bReq, bmRType_bReq, wValue, wIndex, wLength);
5692731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5702731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Request Destination:
5712731b9a8SJean-Christophe PLAGNIOL-VILLARD 		   without flags: Device,
5722731b9a8SJean-Christophe PLAGNIOL-VILLARD 		   USB_RECIP_INTERFACE: interface,
5732731b9a8SJean-Christophe PLAGNIOL-VILLARD 		   USB_RECIP_ENDPOINT: endpoint,
5742731b9a8SJean-Christophe PLAGNIOL-VILLARD 		   USB_TYPE_CLASS means HUB here,
5752731b9a8SJean-Christophe PLAGNIOL-VILLARD 		   USB_RECIP_OTHER | USB_TYPE_CLASS  almost ever means HUB_PORT here
5762731b9a8SJean-Christophe PLAGNIOL-VILLARD 	*/
5772731b9a8SJean-Christophe PLAGNIOL-VILLARD 	switch (bmRType_bReq) {
5782731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case RH_GET_STATUS:
5792731b9a8SJean-Christophe PLAGNIOL-VILLARD 		*(__u16 *)bufp = cpu_to_le16(1);
5802731b9a8SJean-Christophe PLAGNIOL-VILLARD 		OK(2);
5812731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5822731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case RH_GET_STATUS | USB_RECIP_INTERFACE:
5832731b9a8SJean-Christophe PLAGNIOL-VILLARD 		*(__u16 *)bufp = cpu_to_le16(0);
5842731b9a8SJean-Christophe PLAGNIOL-VILLARD 		OK(2);
5852731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5862731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case RH_GET_STATUS | USB_RECIP_ENDPOINT:
5872731b9a8SJean-Christophe PLAGNIOL-VILLARD 		*(__u16 *)bufp = cpu_to_le16(0);
5882731b9a8SJean-Christophe PLAGNIOL-VILLARD 		OK(2);
5892731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5902731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case RH_GET_STATUS | USB_TYPE_CLASS:
5912731b9a8SJean-Christophe PLAGNIOL-VILLARD 		*(__u32 *)bufp = cpu_to_le32(0);
5922731b9a8SJean-Christophe PLAGNIOL-VILLARD 		OK(4);
5932731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5942731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case RH_GET_STATUS | USB_RECIP_OTHER | USB_TYPE_CLASS:
5952731b9a8SJean-Christophe PLAGNIOL-VILLARD 		*(__u32 *)bufp = cpu_to_le32(rh_status.wPortChange<<16 | rh_status.wPortStatus);
5962731b9a8SJean-Christophe PLAGNIOL-VILLARD 		OK(4);
5972731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5982731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case RH_CLEAR_FEATURE | USB_RECIP_ENDPOINT:
5992731b9a8SJean-Christophe PLAGNIOL-VILLARD 		switch (wValue) {
6002731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case 1:
6012731b9a8SJean-Christophe PLAGNIOL-VILLARD 			OK(0);
6022731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
6032731b9a8SJean-Christophe PLAGNIOL-VILLARD 		break;
6042731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6052731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case RH_CLEAR_FEATURE | USB_TYPE_CLASS:
6062731b9a8SJean-Christophe PLAGNIOL-VILLARD 		switch (wValue) {
6072731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case C_HUB_LOCAL_POWER:
6082731b9a8SJean-Christophe PLAGNIOL-VILLARD 			OK(0);
6092731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6102731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case C_HUB_OVER_CURRENT:
6112731b9a8SJean-Christophe PLAGNIOL-VILLARD 			OK(0);
6122731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
6132731b9a8SJean-Christophe PLAGNIOL-VILLARD 		break;
6142731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6152731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case RH_CLEAR_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
6162731b9a8SJean-Christophe PLAGNIOL-VILLARD 		switch (wValue) {
6172731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_ENABLE:
6182731b9a8SJean-Christophe PLAGNIOL-VILLARD 			rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE;
6192731b9a8SJean-Christophe PLAGNIOL-VILLARD 			OK(0);
6202731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6212731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_SUSPEND:
6222731b9a8SJean-Christophe PLAGNIOL-VILLARD 			rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND;
6232731b9a8SJean-Christophe PLAGNIOL-VILLARD 			OK(0);
6242731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6252731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_POWER:
6262731b9a8SJean-Christophe PLAGNIOL-VILLARD 			rh_status.wPortStatus &= ~USB_PORT_STAT_POWER;
6272731b9a8SJean-Christophe PLAGNIOL-VILLARD 			OK(0);
6282731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6292731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_C_CONNECTION:
6302731b9a8SJean-Christophe PLAGNIOL-VILLARD 			rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION;
6312731b9a8SJean-Christophe PLAGNIOL-VILLARD 			OK(0);
6322731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6332731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_C_ENABLE:
6342731b9a8SJean-Christophe PLAGNIOL-VILLARD 			rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE;
6352731b9a8SJean-Christophe PLAGNIOL-VILLARD 			OK(0);
6362731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6372731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_C_SUSPEND:
6382731b9a8SJean-Christophe PLAGNIOL-VILLARD 			rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND;
6392731b9a8SJean-Christophe PLAGNIOL-VILLARD 			OK(0);
6402731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6412731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_C_OVER_CURRENT:
6422731b9a8SJean-Christophe PLAGNIOL-VILLARD 			rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT;
6432731b9a8SJean-Christophe PLAGNIOL-VILLARD 			OK(0);
6442731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6452731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_C_RESET:
6462731b9a8SJean-Christophe PLAGNIOL-VILLARD 			rh_status.wPortChange &= ~USB_PORT_STAT_C_RESET;
6472731b9a8SJean-Christophe PLAGNIOL-VILLARD 			OK(0);
6482731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
6492731b9a8SJean-Christophe PLAGNIOL-VILLARD 		break;
6502731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6512731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case RH_SET_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
6522731b9a8SJean-Christophe PLAGNIOL-VILLARD 		switch (wValue) {
6532731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_SUSPEND:
6542731b9a8SJean-Christophe PLAGNIOL-VILLARD 			rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND;
6552731b9a8SJean-Christophe PLAGNIOL-VILLARD 			OK(0);
6562731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6572731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_RESET:
6582731b9a8SJean-Christophe PLAGNIOL-VILLARD 			rh_status.wPortStatus |= USB_PORT_STAT_RESET;
6592731b9a8SJean-Christophe PLAGNIOL-VILLARD 			rh_status.wPortChange = 0;
6602731b9a8SJean-Christophe PLAGNIOL-VILLARD 			rh_status.wPortChange |= USB_PORT_STAT_C_RESET;
6612731b9a8SJean-Christophe PLAGNIOL-VILLARD 			rh_status.wPortStatus &= ~USB_PORT_STAT_RESET;
6622731b9a8SJean-Christophe PLAGNIOL-VILLARD 			rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
6632731b9a8SJean-Christophe PLAGNIOL-VILLARD 			OK(0);
6642731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6652731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_POWER:
6662731b9a8SJean-Christophe PLAGNIOL-VILLARD 			rh_status.wPortStatus |= USB_PORT_STAT_POWER;
6672731b9a8SJean-Christophe PLAGNIOL-VILLARD 			OK(0);
6682731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6692731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_ENABLE:
6702731b9a8SJean-Christophe PLAGNIOL-VILLARD 			rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
6712731b9a8SJean-Christophe PLAGNIOL-VILLARD 			OK(0);
6722731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
6732731b9a8SJean-Christophe PLAGNIOL-VILLARD 		break;
6742731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6752731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case RH_SET_ADDRESS:
6762731b9a8SJean-Christophe PLAGNIOL-VILLARD 		root_hub_devnum = wValue;
6772731b9a8SJean-Christophe PLAGNIOL-VILLARD 		OK(0);
6782731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6792731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case RH_GET_DESCRIPTOR:
6802731b9a8SJean-Christophe PLAGNIOL-VILLARD 		switch ((wValue & 0xff00) >> 8) {
6812731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_DT_DEVICE:
6822731b9a8SJean-Christophe PLAGNIOL-VILLARD 			len = sizeof(sl811_rh_dev_des);
6832731b9a8SJean-Christophe PLAGNIOL-VILLARD 			bufp = sl811_rh_dev_des;
6842731b9a8SJean-Christophe PLAGNIOL-VILLARD 			OK(len);
6852731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6862731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_DT_CONFIG:
6872731b9a8SJean-Christophe PLAGNIOL-VILLARD 			len = sizeof(sl811_rh_config_des);
6882731b9a8SJean-Christophe PLAGNIOL-VILLARD 			bufp = sl811_rh_config_des;
6892731b9a8SJean-Christophe PLAGNIOL-VILLARD 			OK(len);
6902731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6912731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_DT_STRING:
6922731b9a8SJean-Christophe PLAGNIOL-VILLARD 			len = usb_root_hub_string(wValue & 0xff, (int)(long)0,	"SL811HS", data, wLength);
6932731b9a8SJean-Christophe PLAGNIOL-VILLARD 			if (len > 0) {
6942731b9a8SJean-Christophe PLAGNIOL-VILLARD 				bufp = data;
6952731b9a8SJean-Christophe PLAGNIOL-VILLARD 				OK(len);
6962731b9a8SJean-Christophe PLAGNIOL-VILLARD 			}
6972731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6982731b9a8SJean-Christophe PLAGNIOL-VILLARD 		default:
6992731b9a8SJean-Christophe PLAGNIOL-VILLARD 			status = -32;
7002731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
7012731b9a8SJean-Christophe PLAGNIOL-VILLARD 		break;
7022731b9a8SJean-Christophe PLAGNIOL-VILLARD 
7032731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case RH_GET_DESCRIPTOR | USB_TYPE_CLASS:
7042731b9a8SJean-Christophe PLAGNIOL-VILLARD 		len = sizeof(sl811_rh_hub_des);
7052731b9a8SJean-Christophe PLAGNIOL-VILLARD 		bufp = sl811_rh_hub_des;
7062731b9a8SJean-Christophe PLAGNIOL-VILLARD 		OK(len);
7072731b9a8SJean-Christophe PLAGNIOL-VILLARD 
7082731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case RH_GET_CONFIGURATION:
7092731b9a8SJean-Christophe PLAGNIOL-VILLARD 		bufp[0] = 0x01;
7102731b9a8SJean-Christophe PLAGNIOL-VILLARD 		OK(1);
7112731b9a8SJean-Christophe PLAGNIOL-VILLARD 
7122731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case RH_SET_CONFIGURATION:
7132731b9a8SJean-Christophe PLAGNIOL-VILLARD 		OK(0);
7142731b9a8SJean-Christophe PLAGNIOL-VILLARD 
7152731b9a8SJean-Christophe PLAGNIOL-VILLARD 	default:
7162731b9a8SJean-Christophe PLAGNIOL-VILLARD 		PDEBUG(1, "unsupported root hub command\n");
7172731b9a8SJean-Christophe PLAGNIOL-VILLARD 		status = -32;
7182731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
7192731b9a8SJean-Christophe PLAGNIOL-VILLARD 
7202731b9a8SJean-Christophe PLAGNIOL-VILLARD 	len = min(len, buf_len);
7212731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (data != bufp)
7222731b9a8SJean-Christophe PLAGNIOL-VILLARD 		memcpy(data, bufp, len);
7232731b9a8SJean-Christophe PLAGNIOL-VILLARD 
7242731b9a8SJean-Christophe PLAGNIOL-VILLARD 	PDEBUG(5, "len = %d, status = %d\n", len, status);
7252731b9a8SJean-Christophe PLAGNIOL-VILLARD 
7262731b9a8SJean-Christophe PLAGNIOL-VILLARD 	usb_dev->status = status;
7272731b9a8SJean-Christophe PLAGNIOL-VILLARD 	usb_dev->act_len = len;
7282731b9a8SJean-Christophe PLAGNIOL-VILLARD 
7292731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return status == 0 ? len : status;
7302731b9a8SJean-Christophe PLAGNIOL-VILLARD }
731