Source to bsd/dev/ppc/drvUSBCMD/Library/USLhubs.c


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

/*
	File:		USLhubs.c

	Contains:	Hub specific functions for the USL (mainly add and subtract devices)

	Version:	Nepturn 1.0

	Copyright:	� 1997-1998 by Apple Computer, Inc., all rights reserved.

	File Ownership:

		DRI:				Barry Twycross

		Other Contact:		xxx put other contact here xxx

		Technology:			USB

	Writers:

		(DF)	David Ferguson
		(CJK)	Craig Keithley
		(BT)	Barry Twycross
		
	Change History (most recent first):

	 <USB40>	 11/4/98	BT		Comment inobvious zering of refCon
	 <USB39>	10/29/98	BT		Close old configurations on reset or reconfiguration.
	 <USB38>	 10/7/98	BT		Fix hub not killing children
	 <USB37>	 10/3/98	BT		Add more port reset stuff
	 <USB36>	 9/29/98	BT		Use real frame timing
	 <USB35>	 9/28/98	BT		Add device reset function
	 <USB34>	 9/17/98	BT		1.1 rules for captive devices.
	 <USB33>	 8/25/98	BT		Isoc name changes
	 <USB32>	 8/24/98	BT		Cope without #define for reserved4
	 <USB31>	 8/13/98	BT		Add multibus support
	 <USB30>	 7/10/98	BT		DO secondary interrupt right, set address fail returns ref 0.
	 <USB29>	  7/9/98	BT		Fix previous fixes. Secondary interrupt does not queue secondary
									interrupt
	 <USB28>	  7/9/98	BT		Clean up queues when device deleted
	 <USB27>	 6/15/98	BT		Cope with 1.1 hubs speed detection
	 <USB26>	 6/14/98	DF		Add RemoveRootHub function
	 <USB25>	  6/5/98	BT		Pass pb to delete device
	 <USB24>	 5/20/98	BT		Version 2 PB new names
	 <USB23>	 4/29/98	BT		FIx negative timeout
	 <USB22>	 4/26/98	BT		FIx timeout adding dead device
	 <USB21>	 4/23/98	BT		Add watchdog
	 <USB20>	 4/21/98	BT		Allow requests to device zero, pipe zero.
	 <USB19>	 4/16/98	BT		Eliminate debugger
	 <USB18>	 4/14/98	BT		Impliment device remove
	 <USB17>	 4/10/98	BT		Add device remove
	 <USB16>	  4/9/98	CJK		remove uimpriv.h and uslpriv.h
	 <USB15>	  4/9/98	BT		Use USB.h
		<14>	  4/8/98	BT		More error checking
		<13>	  4/6/98	BT		Change w names
		<12>	  4/6/98	BT		New param block names
		<11>	  4/2/98	BT		Eliminate obsolete delay function
		<10>	 2/23/98	BT		Only add device if set address was sucessful
		 <9>	  2/4/98	BT		Fix uslControlPacket to have errors. Add more support for
									usbProbe
		 <8>	 1/26/98	BT		Mangle names after design review
		 <7>	 1/15/98	CJK		Change include of USL.h to USBServicesLib.h
		 <6>	 1/13/98	BT		Change uslPacket to uslControlPacket
		 <5>	12/19/97	BT		UIM now a Shared lib
		 <4>	12/18/97	BT		Fix header dependancies and add expert call back
*/

#include "../USB.h"
#include "../USBpriv.h"
                
#include "../uimpriv.h"
#include "uslpriv.h"   
                
#include "../driverservices.h"


static QHdrPtr hubAddQueue;
static unsigned long hubAddFlag = 0;
static USBDeviceRef deviceZero;
static UInt32 busZero;
static Duration lastAdd;
static USBPB timeout={0,0, sizeof(USBPB), kUSBCurrentPBVersion};

void initialiseHubs(void)
{
	if( (PBQueueCreate(&hubAddQueue) != noErr) ||
		(PBQueueInit(hubAddQueue) != noErr) )
	{
		/* Panic */
		USBExpertStatus(0,"USL - failed to initialise hub queue", 0);
	}
}

void finaliseHubs(void)
{
	// really need to count the busses!
	//RemoveRootHub(0);	// This should only happen if bus is removed????
	//PBQueueDelete(hubAddQueue);
}

Boolean uslHubValidateDevZero(USBDeviceRef ref, UInt32 *bus)
{
	if(bus != nil)
	{
		*bus = busZero;
	}
	return(ref == deviceZero);
}

OSStatus USBHubConfigurePipeZero(USBPB *pb)
{
UInt32 bus=0;
	if(!checkPBVersion(pb, 1))
	{
		return(pb->usbStatus);
	}
	if(pb->pbVersion == 2)	// v1.00+ only
	{
		pb->usbStatus = kUSBPBVersionError;
		return(pb->usbStatus);
	}
	if(!uslHubValidateDevZero(pb->usbReference, &bus))
	{
		pb->usbStatus = kUSBUnknownDeviceErr;
		return(pb->usbStatus);		
	}

kprintf("USBHubConfigurePipeZero:calling UIMControlEDCreate\n");
	pb->usbStatus = UIMControlEDCreate(bus, 0, 0, pb->usb.cntl.WValue, pb->usbFlags);
	(*pb->usbCompletion)(pb);	
	return(kUSBPending);
}


void uslCleanHubQueue(USBDeviceRef ref)
{	// This is assuming its called at secondary interrupt time.
	uslCleanAQueue(hubAddQueue, ref);
}

static void processAddQ(void)
{
OSStatus err;
USBPB *pb;
	if(CompareAndSwap(0, 1, &hubAddFlag))
	{	/* We now have the queue*/
		if(PBDequeueFirst(hubAddQueue, (void *)&pb) == noErr)
		{

			// Could validate ref here.
			
			busZero = pb->usbOther;
			lastAdd = deltaFrames(0, busZero);
			if(pb->pbVersion == 2)
			{
kprintf("processAddQ:calling UIMControlEDCreate\n");
				err = UIMControlEDCreate(busZero, 0, 0, 8, pb->usbFlags);
			/* Error checking ??? */
			}
			
			deviceZero = MakeDevRef(busZero, 0);
			pb->usbReference = deviceZero;
			pb->usbStatus = noErr;
			(*pb->usbCompletion)(pb);
		}
	}
}

/* Takes bus in reference */

void uslHubAddDevice(USBPB *pb)
{
	pb->qType = kUSBHubQType;
	pb->usbStatus = kUSBPending;
	PBEnqueueLast((void *)pb, hubAddQueue);
	processAddQ();
}

OSStatus USBHubAddDevice(USBPB *pb)
{
UInt32 bus;

	pb->usbStatus = validateRef(pb->usbReference, &bus);
	if(pb->usbStatus != noErr)
	{
		return(pb->usbStatus);
	}

	if(!checkPBVersion(pb, 1))
	{
		return(pb->usbStatus);
	}

	pb->usbOther = bus;
	uslHubAddDevice(pb);
	
	return(kUSBPending);
}

/* Takes bus in reference */

static void setAddressHandler(USBPB *pb)
{
UInt32 bus;
UInt32 power;

	bus = busZero;

//	UIMEDDelete(0, 0, 0);
kprintf("setAddressHandler: calling UIMControlEDDelete\n");
	UIMControlEDDelete(bus, 0, 0);
	deviceZero = 0;
	lastAdd = 0;
	
	if(pb->reserved2 != nil)
	{	/* real user call back */
		if(pb->usbStatus == noErr)
		{
			power = ( (pb->usbFlags & kUSBHubPower) != 0)?pb->usbOther:kUSB500mAAvailable;
			if((pb->usbFlags & kUSBHubReaddress) == 0)
			{
				/* Note, this is relying on having usbValue swapped by uiminterface */
				pb->usbReference = addNewDevice(bus, pb->usb.cntl.WValue,  pb->usbFlags&1, pb->wValueStash, power);
			}
		}
		else
		{
			pb->usbReference = 0;
			USBExpertStatus(0, "USL - Error setting address:", pb->usbReference);
		}
	
		pb->usbCompletion = (void *)pb->reserved2;
		(*pb->usbCompletion)(pb);
	}
	else
	{	/* timeout call back */
	
		/* This really is refcon, not reference, this is not a bug. */
		/* This is the call back from the watchdog timer */
		pb->usbRefcon = 0;
	}	
	CompareAndSwap(1, 0, &hubAddFlag);	/* Unlock the queue */
	
	if(hubAddQueue->qHead != nil)
	{
		processAddQ();
	}
}



static void uslHubSetAddress(USBPB *pb)
{
OSStatus err;
	pb->reserved2 = (UInt32) pb->usbCompletion;
	pb->usbCompletion = setAddressHandler;
	pb->wValueStash = pb->usb.cntl.WValue;

	/* Note this does not have to faff around with */
	/* pbVersion etc its writing a packet directly */
	pb->usb.cntl.BMRequestType = 0;	/* bmRqTyp = 0, bRq = 5 */
	pb->usb.cntl.BRequest = kUSBRqSetAddress;	/* bmRqTyp = 0, bRq = 5 */

	if(pb->usbFlags & kUSBHubReaddress)
	{
	usbDevice *d;
		d = getDevicePtr(pb->usbReference);
		if(d == nil)
		{
			pb->usbStatus = kUSBUnknownDeviceErr;
			setAddressHandler(pb);
			return;
		}
		pb->usb.cntl.WValue = HostToUSBWord(d->usbAddress);
	}
	else
	{
		pb->usb.cntl.WValue = HostToUSBWord(getNewAddress());
	}
	pb->usb.cntl.WIndex = 0;
	pb->usb.cntl.reserved4 = 0;					/* zero length */
	
	pb->usbStatus = kUSBPending;
	err = usbControlPacket(busZero, pb, 0, 0);

	if(err != noErr)
	{	/* Even if error, we need to go through handler */
		pb->usbStatus = err;
		setAddressHandler(pb);
	}
}

OSStatus USBHubSetAddress(USBPB *pb)
{
	if(!checkPBVersion(pb, 1|kUSBHubPower|kUSBHubReaddress))
	{
		return(pb->usbStatus);
	}
	
	if(pb->usbFlags & kUSBHubReaddress)
	{
		if(getDevicePtr(pb->usbReference) == nil)
		{
			pb->usbStatus = kUSBUnknownDeviceErr;
			return(pb->usbStatus);
		}
	}
	else
	{	
		if(!uslHubValidateDevZero(pb->usbReference, nil))
		{
			pb->usbReference = 0;
			pb->usbStatus = paramErr;
			return(pb->usbStatus);
		}
	}

	uslHubSetAddress(pb);
	
	return(kUSBPending);
}

static OSStatus secondaryCleanInternalQueues(void *p1, void *p2)
{	// Any new queues should put an entry in here 
USBDeviceRef ref;
	p2=0;
	ref = (USBDeviceRef)p1;
	
	uslCleanHubQueue(ref);
	uslCleanDelayQueue(ref);
	uslCleanNotifyQueue(ref);
	uslCleanMemQueue(ref);
	
	return(noErr);	// no one's listening
}

static OSStatus uslCleanInternalQueues(USBDeviceRef ref)
{
	return(CallSecondaryInterruptHandler2(secondaryCleanInternalQueues, nil, (void *)ref, 0));
}

OSStatus USBHubDeviceRemoved(USBPB *pb)
{
OSStatus err = kUSBPending;
usbDevice *deviceP;
OSStatus retVal = noErr;
Boolean noCallBack;

	noCallBack = (pb->usbCompletion == kUSBNoCallBack);
	if(noCallBack)
	{
		pb->usbCompletion = (void *)-2;
	}
	if(!checkPBVersion(pb, 0))
	{
		return(pb->usbStatus);
	}
	if(noCallBack)
	{
		pb->usbCompletion = kUSBNoCallBack;
	}

	deviceP = getDevicePtr(pb->usbReference);
	if(deviceP == nil)
	{
		pb->usbStatus = kUSBUnknownDeviceErr;
		return(kUSBUnknownDeviceErr);
	}
	
	// Must do this before pipes are closed.
	retVal = uslCleanInternalQueues(deviceP->ID);
	if(retVal != noErr)	// if this returns an erro we're hosed.
	{
		return(retVal);
	}

	uslClosePipe(deviceP->pipe0);
	
	uslCloseNonDefaultPipes(deviceP);
	
	return(uslDeleteDevice(pb, deviceP));
}

void uslHubWatchDog(Duration currentMilliSec)
{
USBReference currDevZero;
UInt32 bus;

	if((hubAddQueue->qHead != nil) && (lastAdd != 0) )
	{
		currDevZero = deviceZero;
		bus = deviceZero;
		if(lastAdd < currentMilliSec - 1000)
		{	/* Candidate for removal */
		
			if(CompareAndSwap(currDevZero, 0, (void *)&deviceZero))
			{	/* if we're here, deviceZero didn't change since we checked the time */
				/* We've also just diabled that device zero */
				if(timeout.usbRefcon != 0)
				{
					USBExpertStatus(0, "USL hubs - force set address timed out", 0);
					/* timeout in use already */
kprintf("uslHubWatchDog: calling UIMControlEDDelete\n");
					UIMControlEDDelete(bus, 0, 0);
					timeout.usbRefcon = 0;
				}
				else
				{
					USBExpertStatus(0, "USL hubs - timing out add", 0);
					timeout.usbRefcon = 1;
					lastAdd = currentMilliSec + 1000;
					uslHubSetAddress(&timeout);
				}
			}
		
		}
	}
}