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