1764 lines
50 KiB
C
1764 lines
50 KiB
C
/* usb2Mse.c - USB Mouse class driver using the WRS USB2 API */
|
|
|
|
/*
|
|
* Copyright (c) 2008-2011, 2013, 2015, 2018 Wind River Systems, Inc.
|
|
*
|
|
* The right to copy, distribute, modify, or otherwise make use
|
|
* of this software may be licensed only pursuant to the terms
|
|
* of an applicable Wind River license agreement.
|
|
*/
|
|
|
|
|
|
/*
|
|
modification history
|
|
--------------------
|
|
01z,28aug18,whu Unblock the task pended on hInSem semaphore when close the
|
|
device (VXW6-86870)
|
|
01y,25feb15,j_x Correct the invalid statement of iosDrvInstall return (VXW6-84185)
|
|
01x,02jul13,wyy Remove compiler warning (WIND00423315)
|
|
01w,28apr13,ghs Add support of USB message (WIND00411952)
|
|
01v,04jan13,s_z Remove compiler warning (WIND00390357)
|
|
01u,01aug11,ghs Fix VxWorks version check macro (WIND00287708)
|
|
01t,22nov10,ghs Code Coverity CID(9703): CHECKED_RETURN (WIND00242477)
|
|
01s,06jul10,m_y Set all the resources to NULL after release (WIND00183499)
|
|
01r,30jun10,ghs Change display log when device remove(WIND00216586)
|
|
01q,24may10,m_y Add parameter valid check for usb robustness (WIND00183499)
|
|
01p,18may10,w_x USB transition to vxbDmaBufLib supporting 64 bit (WIND00193267)
|
|
01o,27apr10,j_x Add usb2HidProtoGet\Set ioctl for usbGen2Show (WIND00184395)
|
|
01n,30mar10,w_x Back port GEN2 class drivers for old VxWorks (WIND00192167)
|
|
01m,16mar10,pkr Get buffer size from descriptor. Some mice do not work with
|
|
buffer size 3 (WIND00203885)
|
|
01l,03mar10,j_x Changed for USB debug (WIND00184542)
|
|
01k,04mar10,m_y Cancel URB at close routine.(WIND00200272)
|
|
01j,13jan10,ghs vxWorks 6.9 LP64 adapting
|
|
01i,03nov09,w_x Fix URB callback and control URB cancel deadlock (WIND00189102)
|
|
01h,29oct09,ghs Change default debug level(WIND00188349)
|
|
01g,21oct09,w_x Modifications for Kernel API references (WIND00187268)
|
|
01f,27aug09,w_x Unify log mechanism and variable naming with some compiler
|
|
warning removal in USB GEN2 class drivers (WIND00178936)
|
|
01e,25aug09,w_x make sure selWakeupListTerm is called if registered
|
|
(WIND00175211)
|
|
01d,23apr09,w_x Coding convention changes
|
|
01c,26feb09,w_x Remove dead lock condition in usb2MseDeviceRemove
|
|
01b,24feb09,w_x Remove dead lock conditon in usb2MseDeviceAdd
|
|
01a,05Nov08,adh written
|
|
*/
|
|
|
|
/*
|
|
DESCRIPTION
|
|
|
|
This module implements the USB mouse class driver for the vxWorks operating
|
|
system. This is a character driver and is written using the Wind River USB
|
|
2.x API.
|
|
|
|
INITIALIZATION
|
|
|
|
This driver is initialized by calling usb2MseInit(), with a name parameter
|
|
specifying the default name of the device. The driver will register with the
|
|
USBD and thus will detect any existing connections as well as future device
|
|
connections.
|
|
|
|
Prior to calling usb2MseInit(), the caller must ensure that the USBD has been
|
|
properly initialized.
|
|
|
|
It is also the caller's responsibility to ensure that at least one USB HCD
|
|
(USB Host Controller Driver) is attached to the USBD before the mouse
|
|
operation can begin.
|
|
|
|
OTHER FUNCTIONS
|
|
|
|
usb2Mse.c also supports the ioctl interface. The following ioctl codes
|
|
are supported:
|
|
|
|
\ml
|
|
|
|
\m 1. FIONREAD
|
|
get num chars available to read.
|
|
|
|
\m 2. FIOSELECT
|
|
register the task for waking up when data is available.
|
|
|
|
\m 3. FIOUNSELECT
|
|
deregister the task for waking up when data is available.
|
|
|
|
\m 4. USB2_IOCTL_HID_IDLE_TIME_GET
|
|
get the current idle time as specified in the HID specification.
|
|
|
|
\m 5. USB2_IOCTL_HID_IDLE_TIME_SET
|
|
set the idle time as specified in the HID specification.
|
|
\me
|
|
|
|
DATA FLOW
|
|
|
|
For each USB mouse connected to the system, the USBD will attach each
|
|
interface of the device to the appropriate class driver deviceAdd routine.
|
|
The device is then mounted in the iosLib, and the application can start doing
|
|
I/O requsts to the device driver.
|
|
|
|
INCLUDE FILES: vxWorks.h selectLib.h semLib.h logLib.h errno.h errnoLib.h
|
|
iosLib.h rngLib.h usb2/usb2Mse.h
|
|
*/
|
|
|
|
/* includes */
|
|
|
|
#include <vxWorks.h>
|
|
#include <selectLib.h>
|
|
#include <semLib.h>
|
|
#include <logLib.h>
|
|
#include <errno.h>
|
|
#include <errnoLib.h>
|
|
#include <iosLib.h>
|
|
#include <rngLib.h>
|
|
#include <usb2/usb2Mse.h>
|
|
|
|
/* defines */
|
|
|
|
/* default device name */
|
|
|
|
#ifndef USB2_MSE_DEVICE_BASE_NAME
|
|
#define USB2_MSE_DEVICE_BASE_NAME "/usb2Mse/"
|
|
#endif
|
|
|
|
#ifndef USB2_MSE_DEFAULT_IN_BUFFER_NUM_ELEM
|
|
#define USB2_MSE_DEFAULT_IN_BUFFER_NUM_ELEM 1024
|
|
#endif
|
|
|
|
/* typedefs */
|
|
|
|
/* mouse device structure */
|
|
|
|
typedef struct usb2_mse_device
|
|
{
|
|
DEV_HDR deviceHeader; /* I/O system device header */
|
|
USB2_CLASS_DEVICE * pClassDevice; /* Generic class device info */
|
|
SEM_ID hInSem; /* signaling sem, in transfer*/
|
|
SEM_ID hReadMutex; /* Protect concurrent read */
|
|
USBHST_URB * pInUrb; /* In transfer URB */
|
|
UINT32 uInBufferNbElem; /* In ring buffer elements */
|
|
RING_ID hInRingBuffer; /* In ring buffer handle */
|
|
UINT8 bInEndPointAddr; /* In endpoint addr */
|
|
int deviceTimeout; /* Timeout (ms) to wait reqs. */
|
|
USB2_FILE_DESC * pReader; /* Reader file descriptor */
|
|
USB2_MSE_REPORT * pReport; /* Report structure read */
|
|
UINT16 uReportSize; /* Report structure read size */
|
|
UINT8 uIdleTime; /* Idle time */
|
|
BOOL bTransferInCourse; /* Is a transfer running? */
|
|
SEL_WAKEUP_LIST * pSelWakeupList; /* Select wakeup list */
|
|
BOOL bSelListInitialized; /* above list initialized */
|
|
} USB2_MSE_DEVICE;
|
|
|
|
/* locals */
|
|
|
|
LOCAL int usb2MseDrvNum = 0;
|
|
|
|
/* globals */
|
|
|
|
/*
|
|
* This will contain the Data used to register to the usbd,
|
|
* and some driver info
|
|
*/
|
|
|
|
USB2_CLASS_DRIVER * pUsb2MseDriver = NULL;
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* usb2MseInUrbCompleteCallback - callback called when a in transfer is done
|
|
*
|
|
* This routine is called from the USBD on a interrupt in request
|
|
* completion.
|
|
*
|
|
* RETURNS: USBHST_SUCCESS, or other USBHST_STATUS value if there is any failure.
|
|
*
|
|
* ERRNO: N/A
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL USBHST_STATUS usb2MseInUrbCompleteCallback
|
|
(
|
|
pUSBHST_URB pUrb /* Pointer to the IN URB structure that is completed. */
|
|
)
|
|
{
|
|
USB2_MSE_DEVICE * pDevice = NULL;
|
|
STATUS status = ERROR;
|
|
USBHST_STATUS hstStatus = USBHST_SUCCESS;
|
|
size_t nBytes = 0;
|
|
|
|
if ((NULL == pUrb) || (NULL == pUrb->pContext))
|
|
{
|
|
USB2_MSE_ERR("Invalid Parameter\n", 1, 2, 3, 4, 5, 6);
|
|
return USBHST_INVALID_PARAMETER;
|
|
}
|
|
|
|
USB2_MSE_VDBG("usb2MseInUrbCompleteCallback(): "
|
|
"URB returned with result %p\n",
|
|
pUrb->nStatus, 2, 3, 4, 5, 6);
|
|
|
|
pDevice = (USB2_MSE_DEVICE* ) pUrb->pContext;
|
|
|
|
pDevice->bTransferInCourse = FALSE;
|
|
|
|
status = usb2TakeDevice (pDevice->pClassDevice);
|
|
|
|
if (ERROR == status)
|
|
{
|
|
USB2_MSE_ERR("usb2MseInUrbCompleteCallback(): "
|
|
"Cannot take device ownership\n",1,2,3,4,5,6);
|
|
|
|
return USBHST_FAILURE;
|
|
}
|
|
|
|
/*
|
|
* Fill the buffer for what we have been getting, if the URB
|
|
* has been transferred properly. If there is a transfer ERROR
|
|
* with the URB, that means that THIS URB is not proper,
|
|
* so ignore the content.We should only reporting
|
|
* full packets, do not insert partial packets into the ring
|
|
* buffer.
|
|
*/
|
|
|
|
if ((USBHST_SUCCESS == pUrb->nStatus) &&
|
|
(pUrb->uTransferLength >= sizeof(USB2_MSE_REPORT)))
|
|
{
|
|
nBytes = rngBufPut(pDevice->hInRingBuffer,
|
|
(char*) pDevice->pReport,
|
|
sizeof(USB2_MSE_REPORT));
|
|
|
|
if (pDevice->pReader)
|
|
{
|
|
if (0 < selWakeupListLen(pDevice->pSelWakeupList) )
|
|
{
|
|
selWakeupAll (pDevice->pSelWakeupList, SELREAD);
|
|
}
|
|
}
|
|
|
|
/* Signal the waiting task that the transfer has finished */
|
|
|
|
semGive (pDevice->hInSem);
|
|
|
|
USB2_MSE_DBG("usb2MseInUrbCompleteCallback(): Inserted %d bytes"
|
|
"in the Device Ring buffer, %d bytes available \n",
|
|
nBytes, rngFreeBytes(pDevice->hInRingBuffer), 3, 4, 5, 6);
|
|
}
|
|
|
|
/*
|
|
* If there is no file descriptor,
|
|
* suspend communicating with the device
|
|
*/
|
|
|
|
if (NULL == pDevice->pReader)
|
|
{
|
|
USB2_MSE_DBG("usb2MseInUrbCompleteCallback(): File descriptor has "
|
|
"been closed, return here without submitting \n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2ReleaseDevice(pDevice->pClassDevice);
|
|
|
|
return USBHST_SUCCESS;
|
|
}
|
|
|
|
USBHST_FILL_INTERRUPT_URB(pDevice->pInUrb,
|
|
pDevice->pClassDevice->hDevice,
|
|
pDevice->bInEndPointAddr,
|
|
(UCHAR*) pDevice->pReport,
|
|
pDevice->uReportSize,
|
|
USBHST_SHORT_TRANSFER_OK,
|
|
usb2MseInUrbCompleteCallback,
|
|
(void *) pDevice,
|
|
USBHST_SUCCESS
|
|
);
|
|
|
|
USB2_MSE_VDBG("usb2MseInUrbCompleteCallback(): "
|
|
"Resubmitting URB...\n", 1, 2, 3, 4, 5, 6);
|
|
|
|
/* Prepare requesting data */
|
|
|
|
pDevice->bTransferInCourse = TRUE;
|
|
|
|
hstStatus = usbHstURBSubmit(pDevice->pInUrb);
|
|
|
|
if (USBHST_SUCCESS != hstStatus)
|
|
{
|
|
pDevice->bTransferInCourse = FALSE;
|
|
|
|
/* Something wrong with the device */
|
|
|
|
USB2_MSE_ERR("usb2MseInUrbCompleteCallback():"
|
|
"Submitting URB failed : %d\n", hstStatus, 2, 3, 4, 5, 6);
|
|
}
|
|
|
|
USB2_MSE_VDBG("usb2MseInUrbCompleteCallback(): "
|
|
"URB Submitted with Status %d\n", hstStatus, 2, 3, 4, 5, 6);
|
|
|
|
/* And return */
|
|
|
|
usb2ReleaseDevice(pDevice->pClassDevice);
|
|
|
|
return (hstStatus);
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* usb2MseIosOpen - io open call
|
|
*
|
|
* This routine checks the IO of the device, and creates a file descriptor
|
|
* to allow user application to read the mouse data.
|
|
*
|
|
* RETURNS: pointer to the USB2_FILE_DESC, or ERROR if any failure.
|
|
*
|
|
* ERRNO: EIO, ENOMEM
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL ULONG usb2MseIosOpen
|
|
(
|
|
DEV_HDR * pDevHdr,
|
|
char * notUsed,
|
|
int flags,
|
|
int mode
|
|
)
|
|
{
|
|
USB2_MSE_DEVICE * pDevice = (USB2_MSE_DEVICE * ) pDevHdr;
|
|
USB2_FILE_DESC * pFd; /* File descriptor structure used */
|
|
USBHST_STATUS hstStatus;
|
|
|
|
USB2_MSE_VDBG ("usb2MseIosOpen(): Opening the Mouse : "
|
|
"mode 0x%X, flags 0x%X\n", mode, flags, 3, 4, 5, 6);
|
|
|
|
if (OK != usb2TakeDevice (pDevice->pClassDevice))
|
|
{
|
|
USB2_MSE_ERR ("usb2MseIosOpen(): "
|
|
"Cannot take device ownership\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
errnoSet(EINVAL);
|
|
|
|
return (ULONG)ERROR;
|
|
}
|
|
|
|
/*
|
|
* We don't want the device to be created and do not support
|
|
* write to a mouse device, so refuse these flags
|
|
*/
|
|
|
|
if ((O_RDWR & flags) || (O_WRONLY & flags) || (O_CREAT & flags ))
|
|
{
|
|
USB2_MSE_ERR("usb2MseIosOpen():"
|
|
"Device not writable/creatable \n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2ReleaseDevice(pDevice->pClassDevice);
|
|
|
|
errnoSet(EINVAL);
|
|
|
|
return (ULONG)ERROR;
|
|
}
|
|
|
|
/*
|
|
* And we support only one fd reading at the time, so if the
|
|
* pDevice->pReader is not NULL, then someone has already opened
|
|
* the device, so we should return with ERROR here.
|
|
*/
|
|
|
|
if (NULL != pDevice->pReader)
|
|
{
|
|
USB2_MSE_ERR("usb2MseIosOpen(): Driver only "
|
|
"allows one reader, another fd is open, return error \n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2ReleaseDevice (pDevice->pClassDevice);
|
|
|
|
errnoSet (EINVAL);
|
|
|
|
return (ULONG)ERROR;
|
|
}
|
|
|
|
/* Create the file descriptor structure */
|
|
|
|
pFd = (USB2_FILE_DESC *) OSS_CALLOC (sizeof(USB2_FILE_DESC));
|
|
if (NULL == pFd)
|
|
{
|
|
USB2_MSE_ERR ("usb2MseIosOpen():Cannot allocate data\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2ReleaseDevice(pDevice->pClassDevice);
|
|
|
|
errnoSet(ENOMEM);
|
|
|
|
return (ULONG)ERROR;
|
|
}
|
|
|
|
/* Initialize the file descriptor structure */
|
|
|
|
pFd->pDevice = pDevice;
|
|
|
|
pFd->fdType = (UINT32)flags;
|
|
|
|
pDevice->pReader = pFd;
|
|
|
|
/* Flush the ring buffer so that no previous data is in it */
|
|
|
|
rngFlush(pDevice->hInRingBuffer);
|
|
|
|
USB2_MSE_DBG ("usb2MseIosOpen(): Device set to READ %p \n",
|
|
pFd, 2, 3, 4, 5, 6);
|
|
|
|
USBHST_FILL_INTERRUPT_URB(pDevice->pInUrb,
|
|
pDevice->pClassDevice->hDevice,
|
|
pDevice->bInEndPointAddr,
|
|
(UCHAR*) pDevice->pReport,
|
|
pDevice->uReportSize,
|
|
USBHST_SHORT_TRANSFER_OK,
|
|
usb2MseInUrbCompleteCallback,
|
|
(void *) pDevice,
|
|
USBHST_SUCCESS
|
|
);
|
|
|
|
USB2_MSE_VDBG("usb2MseIosOpen(): Submit an URB to start getting data \n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
/*
|
|
* Submitting the URB starts the I/O on this instance of the mouse
|
|
*/
|
|
|
|
pDevice->bTransferInCourse = TRUE;
|
|
|
|
if ((hstStatus = usbHstURBSubmit(pDevice->pInUrb)) != USBHST_SUCCESS)
|
|
{
|
|
USB2_MSE_ERR("usb2MseIosOpen(): usbHstURBSubmit failed %d\n",
|
|
hstStatus, 2, 3, 4, 5, 6);
|
|
|
|
errnoSet (EIO);
|
|
|
|
pDevice->bTransferInCourse = FALSE;
|
|
|
|
pDevice->pReader = NULL;
|
|
|
|
OSS_FREE (pFd);
|
|
|
|
usb2ReleaseDevice(pDevice->pClassDevice);
|
|
|
|
return (ULONG)ERROR;
|
|
}
|
|
|
|
usb2ReleaseDevice(pDevice->pClassDevice);
|
|
|
|
return (ULONG) pFd;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* usb2MseIosClose - io close call
|
|
*
|
|
* This routine closes the FD to this device.
|
|
*
|
|
* RETURNS: OK, or ERROR if there is any failure.
|
|
*
|
|
* ERRNO: N/A
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL int usb2MseIosClose
|
|
(
|
|
USB2_FILE_DESC * pFd /* File descriptor used by the application */
|
|
)
|
|
{
|
|
USB2_MSE_DEVICE * pDevice = pFd->pDevice;
|
|
|
|
USB2_MSE_DBG("usb2MseIosClose(): Closing the Mouse %s\n",
|
|
pDevice->deviceHeader.name, 2, 3, 4, 5, 6);
|
|
|
|
if (OK != usb2TakeDevice (pDevice->pClassDevice))
|
|
{
|
|
USB2_MSE_ERR("usb2MseIosClose():"
|
|
"Cannot take device ownership\n", 1, 2, 3, 4, 5, 6);
|
|
|
|
return ERROR;
|
|
}
|
|
|
|
if (pDevice->pReader == pFd)
|
|
{
|
|
USB2_MSE_VDBG("usb2MseIosClose(): Reset device reader to NULL.\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
pDevice->pReader = NULL;
|
|
|
|
if (pDevice->hInSem)
|
|
(void) semFlush(pDevice->hInSem);
|
|
}
|
|
|
|
/* Cancel the active URB */
|
|
|
|
(void) usbHstURBCancel(pDevice->pInUrb);
|
|
|
|
OSS_FREE (pFd);
|
|
|
|
usb2ReleaseDevice(pDevice->pClassDevice);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* usb2MseIosRead - io read call
|
|
*
|
|
* This routine reads the device and stores data to the application buffer.
|
|
*
|
|
* RETURNS: ERROR or number of bytes read.
|
|
*
|
|
* ERRNO: EIO
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL ssize_t usb2MseIosRead
|
|
(
|
|
USB2_FILE_DESC * pFd, /* File descriptor used by application */
|
|
char * pBuffer, /* Buffer to read to */
|
|
size_t bfrSize /* Number of bytes to read */
|
|
)
|
|
{
|
|
USB2_MSE_DEVICE * pDevice = pFd->pDevice;
|
|
STATUS status = ERROR;
|
|
size_t nBytes = 0;
|
|
|
|
USB2_MSE_VDBG("usb2MseIosRead(): Start a read %d bytes, to buffer %p\n",
|
|
bfrSize, pBuffer, 3, 4, 5, 6);
|
|
|
|
/* Take the device, and check for usability */
|
|
|
|
status = usb2TakeDevice (pDevice->pClassDevice);
|
|
if (OK != status)
|
|
{
|
|
USB2_MSE_ERR("usb2MseIosRead(): "
|
|
"Cannot take device ownership\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
errnoSet(EIO);
|
|
|
|
return ERROR;
|
|
}
|
|
|
|
/* How many data is already here ? */
|
|
|
|
nBytes = rngNBytes(pDevice->hInRingBuffer);
|
|
|
|
USB2_MSE_VDBG("usb2MseIosRead(): We have %d bytes in the buffer\n",
|
|
nBytes, 2, 3, 4, 5, 6);
|
|
|
|
/*
|
|
* If nBytes is 0, that means that there is nothing
|
|
* in the buffer, so wait for data
|
|
*/
|
|
|
|
if (0 == nBytes)
|
|
{
|
|
status = semTake(pDevice->hReadMutex, WAIT_FOREVER);
|
|
|
|
if (OK != status)
|
|
{
|
|
USB2_MSE_ERR("usb2MseIosRead(): "
|
|
"Cannot take hReadMutex ownership\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
errnoSet(EIO);
|
|
|
|
usb2ReleaseDevice (pDevice->pClassDevice);
|
|
|
|
return ERROR;
|
|
}
|
|
|
|
/* Release the device mutex so that the URB callback can take it */
|
|
|
|
usb2ReleaseDevice (pDevice->pClassDevice);
|
|
|
|
/* Do the actual wait */
|
|
|
|
status = semTake (pDevice->hInSem, WAIT_FOREVER);
|
|
|
|
/* if any error occured during the wait */
|
|
|
|
if (OK != status || pDevice->pReader == NULL)
|
|
{
|
|
USB2_MSE_ERR("usb2MseIosRead(): "
|
|
"Cannot take hInIntSem ownership\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
errnoSet(EIO);
|
|
|
|
semGive(pDevice->hReadMutex);
|
|
|
|
return ERROR;
|
|
}
|
|
|
|
semGive(pDevice->hReadMutex);
|
|
|
|
status = usb2TakeDevice (pDevice->pClassDevice);
|
|
|
|
if (OK != status)
|
|
{
|
|
USB2_MSE_ERR("usb2MseIosRead(): "
|
|
"Cannot take device ownership\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
errnoSet(EIO);
|
|
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
/* Copy to the application buffer the data we have */
|
|
|
|
nBytes = rngBufGet(pDevice->hInRingBuffer, pBuffer, (size_t)bfrSize);
|
|
|
|
usb2ReleaseDevice (pDevice->pClassDevice);
|
|
|
|
return (ssize_t)nBytes;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* usb2MseIosWrite - IO write call
|
|
*
|
|
* This routine writes to the device from the application buffer.
|
|
*
|
|
* RETURNS: ERROR or number of bytes written.
|
|
*
|
|
* ERRNO: EIO
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL ssize_t usb2MseIosWrite
|
|
(
|
|
USB2_FILE_DESC * pFd, /* fd struct returned by open */
|
|
UCHAR * pBuffer, /* application buffer */
|
|
size_t bfrSize /* size to write */
|
|
)
|
|
{
|
|
USB2_MSE_ERR("usb2MseIosWrite(): Not supported for writing to mouse\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
errnoSet(EINVAL);
|
|
|
|
return ERROR;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* usb2MseIosIoctl - ioctl call
|
|
*
|
|
* This routine implements the ioctl call for the USB GEN2 mouse class driver.
|
|
* The following ioctl codes are supported:
|
|
*
|
|
* \ml
|
|
*
|
|
* \m 1. FIONREAD
|
|
* get num chars available to read.
|
|
*
|
|
* \m 2. FIOSELECT
|
|
* register the task for waking up when data is available.
|
|
*
|
|
* \m 3. FIOUNSELECT
|
|
* deregister the task for waking up when data is available.
|
|
*
|
|
* \m 4. USB2_IOCTL_HID_IDLE_TIME_GET
|
|
* get the current idle time as specified in the HID specification.
|
|
*
|
|
* \m 5. USB2_IOCTL_HID_IDLE_TIME_SET
|
|
* set the idle time as specified in the HID specification.
|
|
* \me
|
|
*
|
|
* RETURNS: OK, or ERROR if anything failed.
|
|
*
|
|
* ERRNO: EINVAL
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL int usb2MseIosIoctl
|
|
(
|
|
USB2_FILE_DESC * pFd, /* Filedescriptor to use */
|
|
int function, /* Function to use (see above) */
|
|
void * arg /* Argument to use */
|
|
)
|
|
{
|
|
USBHST_STATUS hstStatus = USBHST_SUCCESS;
|
|
STATUS retVal = ERROR;
|
|
USB2_MSE_DEVICE * pDevice = pFd->pDevice;
|
|
|
|
USB2_MSE_VDBG("usb2MseIosIoctl(): Function 0x%X, argument %p\n",
|
|
function, arg, 3, 4, 5, 6);
|
|
|
|
/* Parameter checkings */
|
|
|
|
switch (function)
|
|
{
|
|
case USB2_IOCTL_HID_IDLE_TIME_GET:
|
|
case USB2_IOCTL_HID_IDLE_TIME_SET:
|
|
case USB2_IOCTL_HID_PROTO_GET:
|
|
case USB2_IOCTL_HID_PROTO_SET:
|
|
case FIONREAD:
|
|
case FIOSELECT:
|
|
case FIOUNSELECT:
|
|
if (NULL == arg)
|
|
{
|
|
USB2_MSE_ERR("usb2MseIosIoctl(): invalid parameter\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
errnoSet (EINVAL);
|
|
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
retVal = usb2TakeDevice (pDevice->pClassDevice);
|
|
|
|
if (OK != retVal)
|
|
{
|
|
USB2_MSE_ERR("usb2MseIosIoctl(): "
|
|
"Cannot take device ownership\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
return ERROR;
|
|
}
|
|
|
|
if ((function == USB2_IOCTL_HID_IDLE_TIME_GET) ||
|
|
(function == USB2_IOCTL_HID_IDLE_TIME_SET) ||
|
|
(function == USB2_IOCTL_HID_PROTO_GET) ||
|
|
(function == USB2_IOCTL_HID_PROTO_SET))
|
|
{
|
|
/*
|
|
* NOTE:
|
|
*
|
|
* These functions need to submit URBs on the control pipe,
|
|
* in the mean time the data pipe URB may complete and
|
|
* also need to take the device mutex, thus it is possible
|
|
* to have dead lock condition. To remove the possible
|
|
* dead lock, we do not take device mutex while waiting for
|
|
* control pipe to complete, but to wait on the control pipe
|
|
* mutex, so these pipes can operate concurrently.
|
|
*/
|
|
|
|
retVal = semTake (pDevice->pClassDevice->hSetupMutex, WAIT_FOREVER);
|
|
|
|
if (OK != retVal)
|
|
{
|
|
|
|
USB2_MSE_ERR("usb2MseIosIoctl(): "
|
|
"Cannot take setup mutex ownership\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
/* Failed to take control pipe mutex, return with failre. */
|
|
|
|
usb2ReleaseDevice (pDevice->pClassDevice);
|
|
|
|
return ERROR;
|
|
}
|
|
/*
|
|
* Release the device so that other pipes can operate
|
|
* while control pipe is waiting for complete.
|
|
*/
|
|
|
|
usb2ReleaseDevice (pDevice->pClassDevice);
|
|
}
|
|
|
|
switch (function)
|
|
{
|
|
case FIONREAD:
|
|
*(size_t *)arg = rngNBytes (pDevice->hInRingBuffer);
|
|
|
|
retVal = OK;
|
|
|
|
/* Release device mutex */
|
|
|
|
usb2ReleaseDevice (pDevice->pClassDevice);
|
|
|
|
break;
|
|
case FIOSELECT:
|
|
selNodeAdd (pDevice->pSelWakeupList, (SEL_WAKEUP_NODE *) arg);
|
|
|
|
if (rngNBytes (pDevice->hInRingBuffer) > 0)
|
|
{
|
|
selWakeup ((SEL_WAKEUP_NODE *) arg);
|
|
}
|
|
|
|
retVal = OK;
|
|
|
|
/* Release device mutex */
|
|
|
|
usb2ReleaseDevice (pDevice->pClassDevice);
|
|
|
|
break;
|
|
case FIOUNSELECT:
|
|
|
|
selNodeDelete (pDevice->pSelWakeupList, (SEL_WAKEUP_NODE *)arg);
|
|
|
|
retVal = OK;
|
|
|
|
/* Release device mutex */
|
|
|
|
usb2ReleaseDevice (pDevice->pClassDevice);
|
|
|
|
break;
|
|
case USB2_IOCTL_HID_IDLE_TIME_GET:
|
|
|
|
USB2_MSE_VDBG("usb2MseIosIoctl():USB2_IOCTL_MSE_IDDLE_TIME_GET\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
hstStatus = usb2HidIdleTimeGet(pDevice->pClassDevice,
|
|
(UINT8*) arg, pDevice->deviceTimeout);
|
|
|
|
retVal = ((USBHST_SUCCESS == hstStatus) ? OK : ERROR);
|
|
|
|
/* Release the control pipe mutex */
|
|
|
|
semGive(pDevice->pClassDevice->hSetupMutex);
|
|
|
|
break;
|
|
case USB2_IOCTL_HID_IDLE_TIME_SET:
|
|
USB2_MSE_VDBG("usb2MseIosIoctl():USB2_IOCTL_MSE_IDDLE_TIME_SET\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
hstStatus = usb2HidIdleTimeSet(pDevice->pClassDevice,
|
|
(UINT16)(*(UINT16 *)arg),
|
|
pDevice->deviceTimeout);
|
|
|
|
retVal = ((USBHST_SUCCESS == hstStatus) ? OK : ERROR);
|
|
|
|
/* Release the control pipe mutex */
|
|
|
|
semGive(pDevice->pClassDevice->hSetupMutex);
|
|
|
|
break;
|
|
|
|
|
|
case USB2_IOCTL_HID_PROTO_GET:
|
|
|
|
hstStatus = usb2HidProtoGet(pDevice->pClassDevice,
|
|
(UINT8*) arg, pDevice->deviceTimeout);
|
|
|
|
retVal = ((USBHST_SUCCESS == hstStatus) ? OK : ERROR);
|
|
|
|
/* Release the control pipe mutex */
|
|
|
|
semGive(pDevice->pClassDevice->hSetupMutex);
|
|
|
|
USB2_MSE_VDBG("IDDLE_PROTO_GET=%d, resualt=0x%x \n",
|
|
(*(UINT8 *)arg), retVal, 3, 4, 5, 6);
|
|
|
|
break;
|
|
|
|
case USB2_IOCTL_HID_PROTO_SET:
|
|
|
|
hstStatus = usb2HidProtoSet(pDevice->pClassDevice,
|
|
(*(UCHAR *)arg), pDevice->deviceTimeout);
|
|
|
|
retVal = ((USBHST_SUCCESS == hstStatus) ? OK : ERROR);
|
|
|
|
/* Release the control pipe mutex */
|
|
|
|
semGive(pDevice->pClassDevice->hSetupMutex);
|
|
|
|
USB2_MSE_VDBG("IDDLE_PROTO_SET=%d, resualt=0x%x \n",
|
|
(*(UCHAR *)arg), retVal, 3, 4, 5, 6);
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
USB2_MSE_WARN("usb2MseIosIoctl(): Not supported ioctl - "
|
|
"(function : 0x%X, argument : %p)\n",
|
|
function, arg, 3, 4, 5, 6);
|
|
|
|
errnoSet(ENOTSUP);
|
|
|
|
retVal = ERROR;
|
|
|
|
/* Release device mutex */
|
|
|
|
usb2ReleaseDevice (pDevice->pClassDevice);
|
|
|
|
break;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* usb2MseDelete - delete device related structures
|
|
*
|
|
* This routine is to delete all the device related structures.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL void usb2MseDelete
|
|
(
|
|
USB2_MSE_DEVICE * pDevice
|
|
)
|
|
{
|
|
if (pDevice)
|
|
{
|
|
if (pDevice->hInSem)
|
|
semDelete(pDevice->hInSem);
|
|
|
|
if (pDevice->hInRingBuffer)
|
|
rngDelete(pDevice->hInRingBuffer);
|
|
|
|
if (pDevice->pInUrb)
|
|
OSS_FREE(pDevice->pInUrb);
|
|
|
|
if (pDevice->pReport)
|
|
OSS_FREE(pDevice->pReport);
|
|
|
|
if (pDevice->hReadMutex)
|
|
{
|
|
/* Mutex should be taken before delete it */
|
|
|
|
semTake(pDevice->hReadMutex, WAIT_FOREVER);
|
|
|
|
semDelete(pDevice->hReadMutex);
|
|
}
|
|
|
|
if (pDevice->pSelWakeupList)
|
|
{
|
|
if (pDevice->bSelListInitialized == TRUE)
|
|
{
|
|
selWakeupListTerm(pDevice->pSelWakeupList);
|
|
}
|
|
OSS_FREE (pDevice->pSelWakeupList);
|
|
}
|
|
|
|
pDevice->hInSem = NULL;
|
|
pDevice->hInRingBuffer = NULL;
|
|
pDevice->pReport = NULL;
|
|
pDevice->pInUrb = NULL;
|
|
pDevice->hReadMutex = NULL;
|
|
pDevice->pSelWakeupList = NULL;
|
|
|
|
if (pDevice->pClassDevice != NULL)
|
|
{
|
|
usb2ClassDeviceDelete(pDevice->pClassDevice);
|
|
|
|
/*
|
|
* The routine usb2ClassDeviceDelete will free the "pDevice->pClassDevice"
|
|
* So we'd better set this pointer to NULL. As we may call
|
|
* usb2ReleaseDevice(pDevice->pClassDevice) after the delete process.
|
|
* If we don't set this pointer to NULL, may exception happened.
|
|
*/
|
|
|
|
pDevice->pClassDevice = NULL;
|
|
}
|
|
|
|
OSS_FREE(pDevice);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* usb2MseDeleteCallback - delete the device if no transfer active
|
|
*
|
|
* This routine is to delete all the device related structures if there is
|
|
* no active transfer on any of the pipes.
|
|
*
|
|
* RETURNS: OK if device deleted, ERROR if transfer still active.
|
|
*
|
|
* ERRNO: N/A
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL STATUS usb2MseDeleteCallback
|
|
(
|
|
USB2_MSE_DEVICE * pDevice
|
|
)
|
|
{
|
|
if (NULL == pDevice)
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeleteCallback(): Device Pointer is NULL\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
return ERROR;
|
|
}
|
|
|
|
/* Check if a transfer is still active */
|
|
|
|
if (TRUE == pDevice->bTransferInCourse)
|
|
return ERROR;
|
|
|
|
if (OK != semTake(pDevice->pClassDevice->hSetupMutex, 0))
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeleteCallback(): Control Transfer STILL active\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
return ERROR;
|
|
}
|
|
|
|
if (OK != semTake(pDevice->hReadMutex,0))
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeleteCallback(): Read mutex taken by others\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
semGive(pDevice->hInSem);
|
|
|
|
return ERROR;
|
|
}
|
|
|
|
usb2MseDelete(pDevice);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* usb2MseCreate - create device related structures
|
|
*
|
|
* This routine creates all the device related structures.
|
|
*
|
|
* RETURNS: pointer to USB2_MSE_DEVICE, or NULL if there is any failure.
|
|
*
|
|
* ERRNO: N/A
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL USB2_MSE_DEVICE * usb2MseCreate
|
|
(
|
|
USB2_CLASS_DEVICE * pUsb2ClassDevice,
|
|
UINT8 bInEndpointAddr,
|
|
UINT16 wMaxPacketSize
|
|
)
|
|
{
|
|
USB2_MSE_DEVICE * pDevice;
|
|
|
|
|
|
pDevice = (USB2_MSE_DEVICE *)OSS_CALLOC (sizeof(USB2_MSE_DEVICE));
|
|
|
|
if (pDevice == NULL)
|
|
{
|
|
USB2_MSE_ERR("usb2MseCreate(): "
|
|
"pDevice data allocation failed\n", 1, 2, 3, 4, 5, 6);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
pDevice->pClassDevice = pUsb2ClassDevice;
|
|
|
|
pDevice->hInSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY);
|
|
|
|
if (NULL == pDevice->hInSem)
|
|
{
|
|
USB2_MSE_ERR("usb2MseCreate(): hInIntSem semaphore creation failed\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2MseDelete(pDevice);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
pDevice->hReadMutex = semMCreate(USB2_MUTEX_CREATION_OPTS);
|
|
|
|
if (NULL == pDevice->hReadMutex)
|
|
{
|
|
USB2_MSE_ERR("usb2MseCreate(): pReadMutex Mutex Creation failed\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2MseDelete(pDevice);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
pDevice->pInUrb = (USBHST_URB *)OSS_CALLOC(sizeof(USBHST_URB));
|
|
|
|
if (pDevice->pInUrb == NULL)
|
|
{
|
|
USB2_MSE_ERR("usb2MseCreate(): pInUrb allocation failed\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2MseDelete(pDevice);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
pDevice->uInBufferNbElem = USB2_MSE_DEFAULT_IN_BUFFER_NUM_ELEM;
|
|
|
|
pDevice->hInRingBuffer = rngCreate(USB2_MSE_DEFAULT_IN_BUFFER_NUM_ELEM
|
|
* sizeof(USB2_MSE_REPORT));
|
|
|
|
if (pDevice->hInRingBuffer == NULL)
|
|
{
|
|
USB2_MSE_ERR("usb2MseCreate(): pInRingBuffer allocation failed\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2MseDelete(pDevice);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if (wMaxPacketSize < sizeof(USB2_MSE_REPORT))
|
|
pDevice->uReportSize = sizeof(USB2_MSE_REPORT);
|
|
else
|
|
pDevice->uReportSize = wMaxPacketSize;
|
|
|
|
|
|
pDevice->pReport = (USB2_MSE_REPORT *) OSS_CALLOC(pDevice->uReportSize);
|
|
if (NULL == pDevice->pReport)
|
|
{
|
|
USB2_MSE_ERR("usb2MseCreate(): pReport allocation failed\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2MseDelete(pDevice);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
pDevice->pSelWakeupList = (SEL_WAKEUP_LIST *)
|
|
OSS_CALLOC(sizeof (SEL_WAKEUP_LIST));
|
|
|
|
if (NULL == pDevice->pSelWakeupList)
|
|
{
|
|
USB2_MSE_ERR("usb2MseCreate(): pSelWakeupList allocation failed\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2MseDelete(pDevice);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
selWakeupListInit (pDevice->pSelWakeupList);
|
|
|
|
pDevice->bSelListInitialized = TRUE;
|
|
|
|
pDevice->bInEndPointAddr = bInEndpointAddr;
|
|
|
|
pDevice->pReader = NULL;
|
|
|
|
pDevice->deviceTimeout = USB2_DEFAULT_TIMEOUT;
|
|
|
|
pDevice->bTransferInCourse = FALSE;
|
|
|
|
return pDevice;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* usb2MseDeviceAdd - device add method of USBD interface
|
|
*
|
|
* This routine is called by the USBD each time a new USB mass storage device
|
|
* is attached to the system. This routine is responsible for identifying the
|
|
* device, configuring it and creating the device structure that enables use
|
|
* of the device.
|
|
*
|
|
* RETURNS: USBHST_SUCCESS, or USBHST_FAILURE if there is any failure.
|
|
*
|
|
* ERRNO: N/A
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL USBHST_STATUS usb2MseDeviceAdd
|
|
(
|
|
UINT32 hDevice, /* device handler returned by usbd */
|
|
UINT8 bInterfaceNumber, /* interface number found by the usbd */
|
|
UINT8 uSpeed, /* speed of the device */
|
|
void ** ppDriverData /* place to put the device data*/
|
|
)
|
|
{
|
|
USB2_MSE_DEVICE * pDevice = NULL;
|
|
STATUS status = ERROR;
|
|
USBHST_STATUS hstStatus = USBHST_FAILURE;
|
|
pUSBD_INTERFACE_INFO pInterface = NULL;
|
|
pUSBD_DEVICE_INFO pDeviceInfo = NULL;
|
|
pUSBHST_CONFIG_DESCRIPTOR pCurrentConfiguration = NULL;
|
|
USB2_CLASS_DEVICE * pUsb2ClassDevice = NULL;
|
|
UINT8 uInEndPointAddr = 0;
|
|
UINT8 configurationIndex = 0;
|
|
UINT8 uAlternateSetting = 0;
|
|
USBHST_INTERFACE_DESCRIPTOR * pInterfaceDesc;
|
|
USBHST_ENDPOINT_DESCRIPTOR * pEndPointDesc;
|
|
UINT16 wMaxPacketSize;
|
|
USB_TRANSFER_SETUP_INFO xferSetup;
|
|
|
|
USB2_MSE_INFO("Attaching mouse "
|
|
"(hDevice 0x%X, interface %d, speed %d)\n",
|
|
hDevice, bInterfaceNumber, uSpeed, 4, 5, 6);
|
|
|
|
/* Get the device information, for the descriptors */
|
|
|
|
usbdTranslateDeviceHandle(hDevice, &pDeviceInfo);
|
|
|
|
if (NULL == pDeviceInfo )
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeviceAdd(): Mouse is apparently "
|
|
"not connected anymore, the USBD pDeviceInfo is NULL\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
return USBHST_FAILURE;
|
|
}
|
|
|
|
pCurrentConfiguration = (pUSBHST_CONFIG_DESCRIPTOR)
|
|
pDeviceInfo->pCurrentConfiguration;
|
|
if (NULL == pCurrentConfiguration)
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeviceAdd(): pCurrentConfiguration NULL\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
return USBHST_FAILURE;
|
|
}
|
|
|
|
configurationIndex = (UINT8)(pCurrentConfiguration->bConfigurationValue -1);
|
|
|
|
if (usbHstSetConfiguration (hDevice,configurationIndex ) != OK)
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeviceAdd(): Failed to set configuration - "
|
|
"device not added.\n", 1, 2, 3, 4, 5, 6);
|
|
|
|
return USBHST_FAILURE;
|
|
}
|
|
|
|
pInterface = &(pDeviceInfo->pInterfacesInfo[bInterfaceNumber]);
|
|
|
|
uAlternateSetting = pInterface->uAlternateSetting;
|
|
|
|
USB2_MSE_VDBG("usb2MseDeviceAdd(): Setting Interface %d \n",
|
|
bInterfaceNumber, 2, 3, 4, 5, 6);
|
|
|
|
|
|
if ((hstStatus = usbHstSetInterface(hDevice,
|
|
bInterfaceNumber,
|
|
uAlternateSetting))
|
|
!= USBHST_SUCCESS)
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeviceAdd(): Failed to set interface - "
|
|
"device not added \n", 1, 2, 3, 4, 5, 6);
|
|
|
|
/*
|
|
* If a device only supports a default setting for the
|
|
* specified interface, then a STALL may be returned in
|
|
* the Status stage of the request. And in this case we
|
|
* should continue as this is a control pipe protocol STALL
|
|
* and will be cleared automatically when the device
|
|
* receives the next setup packet.
|
|
*/
|
|
|
|
if (hstStatus != USBHST_STALL_ERROR)
|
|
return USBHST_FAILURE;
|
|
}
|
|
|
|
USB2_MSE_VDBG("usb2MseDeviceAdd(): Searching for endpoints... \n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
pInterfaceDesc = usb2FindInterface(pDeviceInfo,
|
|
bInterfaceNumber,
|
|
uAlternateSetting);
|
|
if (NULL == pInterfaceDesc)
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeviceAdd(): No such interface"
|
|
" bInterfaceNumber %d, uAlternateSetting %d on device\n",
|
|
bInterfaceNumber, uAlternateSetting, 3, 4, 5, 6);
|
|
|
|
return USBHST_FAILURE;
|
|
}
|
|
|
|
/* Find the must-have interrupt in endpoint */
|
|
|
|
pEndPointDesc = usb2FindEndpoint((UCHAR* )pInterfaceDesc,
|
|
USB2_ATTR_INTERRUPT,
|
|
USB2_ENDPOINT_IN,
|
|
0,
|
|
0,
|
|
0);
|
|
if (NULL == pEndPointDesc)
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeviceAdd(): NO IN endpoint found\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
return USBHST_FAILURE;
|
|
}
|
|
|
|
uInEndPointAddr = pEndPointDesc->bEndpointAddress;
|
|
wMaxPacketSize = OS_UINT16_LE_TO_CPU(pEndPointDesc->wMaxPacketSize);
|
|
|
|
/* Set up the trasnfer requirment */
|
|
|
|
OS_MEMSET(&xferSetup, 0, sizeof(USB_TRANSFER_SETUP_INFO));
|
|
|
|
xferSetup.uMaxTransferSize = max(_CACHE_ALIGN_SIZE, wMaxPacketSize);
|
|
xferSetup.uMaxNumReqests = 1;
|
|
xferSetup.uFlags = 0;
|
|
|
|
hstStatus = usbHstPipePrepare(hDevice,
|
|
pEndPointDesc->bEndpointAddress,
|
|
&xferSetup);
|
|
|
|
if (hstStatus != USBHST_SUCCESS)
|
|
{
|
|
USB2_MSE_ERR("Interrupt IN at 0x%x set up failed\n",
|
|
pEndPointDesc->bEndpointAddress, 2, 3, 4, 5, 6);
|
|
|
|
return USBHST_FAILURE;
|
|
}
|
|
|
|
status = usb2TakeDriver(pUsb2MseDriver);
|
|
if (OK != status)
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeviceAdd(): Driver mutex not taken\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
return USBHST_FAILURE;
|
|
}
|
|
|
|
USB2_MSE_VDBG("usb2MseDeviceAdd(): Building & Filling the "
|
|
"Generic Device structure, driver %p, hDevice 0x%X, config 0x%X, "
|
|
"interface 0x%X, altSetting 0x%X... \n",
|
|
pUsb2MseDriver,
|
|
hDevice,
|
|
configurationIndex,
|
|
bInterfaceNumber,
|
|
uAlternateSetting, 6);
|
|
|
|
pUsb2ClassDevice = usb2ClassDeviceCreate (pUsb2MseDriver,
|
|
hDevice,
|
|
configurationIndex,
|
|
bInterfaceNumber,
|
|
uAlternateSetting);
|
|
if (NULL == pUsb2ClassDevice)
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeviceAdd(): usb2ClassDeviceCreate failed\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2ReleaseDriver(pUsb2MseDriver);
|
|
|
|
return USBHST_FAILURE;
|
|
}
|
|
|
|
USB2_MSE_VDBG("usb2MseDeviceAdd(): Building & Filling the "
|
|
"Mouse structure, pUsb2ClassDevice %p, uInEndPointAddr %p"
|
|
"wMaxPacketSize %x\n", pUsb2ClassDevice,
|
|
uInEndPointAddr, wMaxPacketSize, 4, 5, 6);
|
|
|
|
pDevice = usb2MseCreate(pUsb2ClassDevice, uInEndPointAddr, wMaxPacketSize);
|
|
|
|
if (NULL == pDevice)
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeviceAdd(): usb2MseCreate failed\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2ClassDeviceDelete(pUsb2ClassDevice);
|
|
|
|
pUsb2ClassDevice = NULL;
|
|
|
|
usb2ReleaseDriver(pUsb2MseDriver);
|
|
|
|
return USBHST_FAILURE;
|
|
}
|
|
|
|
usb2ReleaseDriver(pUsb2MseDriver);
|
|
|
|
USB2_MSE_VDBG("usb2MseDeviceAdd(): Setting USB2_HID_PROTO_BOOT\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
hstStatus = usb2HidProtoSet(pDevice->pClassDevice,
|
|
USB2_HID_PROTO_BOOT,
|
|
pDevice->deviceTimeout );
|
|
|
|
if (USBHST_SUCCESS != hstStatus)
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeviceAdd(): Could not Set Boot Protocol\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2MseDelete(pDevice);
|
|
|
|
return USBHST_FAILURE;
|
|
}
|
|
|
|
/*
|
|
* Set the idle time to infinite, so that the mouse reports a change
|
|
* only when a change does exist
|
|
*/
|
|
|
|
hstStatus = usb2HidIdleTimeSet(pDevice->pClassDevice,
|
|
USB2_HID_IDLE_INFINITE,
|
|
pDevice->deviceTimeout);
|
|
|
|
if (USBHST_SUCCESS != hstStatus)
|
|
{
|
|
USB2_MSE_WARN("usb2MseDeviceAdd(): Could not set idle time, continue\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
/* Go and ignore this as it is acceptable for this failure */
|
|
}
|
|
|
|
USB2_MSE_VDBG("usb2MseDeviceAdd(): Adding device %s to IO driver %d\n",
|
|
pDevice->pClassDevice->pDeviceName,
|
|
usb2MseDrvNum,
|
|
3, 4, 5, 6);
|
|
|
|
status = iosDevAdd(&pDevice->deviceHeader,
|
|
pDevice->pClassDevice->pDeviceName,
|
|
usb2MseDrvNum);
|
|
|
|
if (OK != status)
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeviceAdd(): iosDevAdd failed.\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2MseDelete(pDevice);
|
|
|
|
return USBHST_FAILURE;
|
|
}
|
|
|
|
USB2_MSE_INFO("Mouse device %s is up and running...\n",
|
|
pDevice->deviceHeader.name, 2, 3, 4, 5, 6);
|
|
|
|
*ppDriverData = pDevice;
|
|
|
|
usbMsgPost(USBMSG_GEN2_MSE_DEVREADY,
|
|
pDevice,
|
|
pDevice->pClassDevice->pDeviceName);
|
|
|
|
return USBHST_SUCCESS;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* usb2MseDeviceRemove - device remove method of USBD interface
|
|
*
|
|
* This routine is called by the USBD each time a device is removed from the
|
|
* system. This routine is responsible for ensuring all outstanding IO requests
|
|
* are completed or aborted and tearing down the I/O structure.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL void usb2MseDeviceRemove
|
|
(
|
|
UINT32 hDevice, /* Device handler provided by the USBD */
|
|
void * pDriverData /* Driver structure returned by add routine */
|
|
)
|
|
{
|
|
STATUS status = ERROR;
|
|
USB2_MSE_DEVICE * pDevice = pDriverData;
|
|
|
|
USB2_MSE_DBG("Removing mouse (hDevice 0x%X, pDevice %p)\n",
|
|
hDevice,
|
|
pDevice, 3, 4, 5, 6);
|
|
|
|
usbMsgPost(USBMSG_GEN2_MSE_DEVREMOVED,
|
|
pDevice,
|
|
pDevice->pClassDevice->pDeviceName);
|
|
|
|
status = usb2TakeDriver(pUsb2MseDriver);
|
|
if (OK != status)
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeviceRemove(): Cannot take driver ownership\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
return;
|
|
}
|
|
|
|
status = usb2TakeDevice(pDevice->pClassDevice);
|
|
if (OK != status)
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeviceRemove(): "
|
|
"Cannot take device ownership\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2ReleaseDriver(pUsb2MseDriver);
|
|
|
|
return;
|
|
}
|
|
|
|
if (pDevice->bSelListInitialized == TRUE)
|
|
{
|
|
selWakeupListTerm(pDevice->pSelWakeupList);
|
|
|
|
pDevice->bSelListInitialized = FALSE;
|
|
}
|
|
|
|
USB2_MSE_INFO("Delete device %s%d\n",
|
|
pDevice->pClassDevice->pDriver->pDefaultDriverName,
|
|
pDevice->pClassDevice->pNode->uDeviceIndex, 3, 4, 5, 6);
|
|
|
|
if (iosDevDelete(&pDevice->deviceHeader) != OK)
|
|
{
|
|
USB2_MSE_WARN("IOS Dev Delete Fail\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
}
|
|
|
|
usb2DeviceDeleteMsgPost(
|
|
pDevice->pClassDevice,
|
|
usb2MseDeleteCallback,
|
|
pDevice
|
|
);
|
|
|
|
/*
|
|
* Release the device mutex since the usb2MseDeleteCallback
|
|
* will need to take the device mutex in order to delete the
|
|
* mutex with respect to the rule "mutex should be taken before
|
|
* delete it"
|
|
*/
|
|
|
|
usb2ReleaseDevice(pDevice->pClassDevice);
|
|
|
|
usb2ReleaseDriver(pUsb2MseDriver);
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* usb2MseSuspendResume - suspend or resume method of the driver
|
|
*
|
|
* This routine is called by the USBD to suspend or resume the USB device.
|
|
* Right now there is nothing to do, so this routine simply returns.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL void usb2MseSuspendResume
|
|
(
|
|
UINT32 hDevice,
|
|
void * pDriver
|
|
)
|
|
{
|
|
USB2_MSE_WARN("usb2MseSuspendResume(): UNSUPPORTED...\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* usb2MseInit - register the USB Mouse class driver with the USBD
|
|
*
|
|
* This routine is called once to register the mouse class driver with the
|
|
* USBD. Multiple instances of the mouse are supported, the driver
|
|
* shall be initialized only once.
|
|
*
|
|
* RETURNS: OK, or ERROR if the the driver could not be initialized.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
STATUS usb2MseInit
|
|
(
|
|
char * pName /* Default base name that will be used, or NULL if no names */
|
|
)
|
|
{
|
|
USBHST_STATUS hstStatus;
|
|
|
|
USB2_MSE_VDBG("usb2MseInit(): Initializing the mouse driver...\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
/* If already initialized, ignore and return. */
|
|
|
|
if (NULL != pUsb2MseDriver)
|
|
{
|
|
USB2_MSE_WARN("usb2MseInit(): Driver already initialized, return...\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
return OK;
|
|
}
|
|
|
|
/* If no valid parameter is given, take the driver default name. */
|
|
|
|
if (NULL == pName)
|
|
pUsb2MseDriver = usb2ClassDriverCreate (USB2_MSE_DEVICE_BASE_NAME);
|
|
else
|
|
pUsb2MseDriver = usb2ClassDriverCreate (pName);
|
|
|
|
/* If the driver cannot be created, return with ERROR. */
|
|
|
|
if (NULL == pUsb2MseDriver)
|
|
{
|
|
USB2_MSE_ERR("usb2MseInit(): Driver cannot be created\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
return ERROR;
|
|
}
|
|
|
|
if (OK != usb2TakeDriver(pUsb2MseDriver))
|
|
{
|
|
USB2_MSE_ERR("usb2MseInit(): Driver cannot be taken\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2ClassDriverDelete(pUsb2MseDriver);
|
|
pUsb2MseDriver = NULL;
|
|
|
|
return ERROR;
|
|
}
|
|
|
|
USB2_MSE_VDBG("usb2MseInit(): Creating the ioLib driver...\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2MseDrvNum = iosDrvInstall ((DRV_CREATE_PTR) NULL, /* No create routine */
|
|
(DRV_REMOVE_PTR) NULL, /* No delete routine */
|
|
(DRV_OPEN_PTR) usb2MseIosOpen, /* Open routine */
|
|
(DRV_CLOSE_PTR) usb2MseIosClose,/* Close routine */
|
|
(DRV_READ_PTR) usb2MseIosRead, /* Read routine */
|
|
(DRV_WRITE_PTR) usb2MseIosWrite, /* no write routine */
|
|
(DRV_IOCTL_PTR) usb2MseIosIoctl);/* ioctl routine */
|
|
|
|
|
|
if (ERROR == usb2MseDrvNum )
|
|
{
|
|
USB2_MSE_VDBG("usb2MseInit(): IO Driver Not installed\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2ClassDriverDelete(pUsb2MseDriver);
|
|
pUsb2MseDriver = NULL;
|
|
|
|
return USBHST_FAILURE;
|
|
}
|
|
|
|
pUsb2MseDriver->hstDriver.bFlagVendorSpecific = FALSE;
|
|
pUsb2MseDriver->hstDriver.uVendorIDorClass = USB2_HID_CLASS;
|
|
pUsb2MseDriver->hstDriver.uProductIDorSubClass = USBD_NOTIFY_ALL;
|
|
pUsb2MseDriver->hstDriver.uBCDUSBorProtocol = USB2_MSE_PROTOCOL;
|
|
pUsb2MseDriver->hstDriver.addDevice = usb2MseDeviceAdd;
|
|
pUsb2MseDriver->hstDriver.removeDevice = usb2MseDeviceRemove;
|
|
pUsb2MseDriver->hstDriver.suspendDevice = usb2MseSuspendResume;
|
|
pUsb2MseDriver->hstDriver.resumeDevice = usb2MseSuspendResume;
|
|
|
|
USB2_MSE_VDBG("usb2MseInit(): Registering the USBD Part:\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
USB2_MSE_VDBG("usb2MseInit(): Vendorspecific : 0x%X :\n",
|
|
pUsb2MseDriver->hstDriver.bFlagVendorSpecific, 2, 3, 4, 5, 6);
|
|
|
|
USB2_MSE_VDBG("usb2MseInit(): uVendorIDorClass : 0x%X :\n",
|
|
pUsb2MseDriver->hstDriver.uVendorIDorClass, 2, 3, 4, 5, 6);
|
|
|
|
USB2_MSE_VDBG("usb2MseInit(): uProductIDorSubClass : 0x%X :\n",
|
|
pUsb2MseDriver->hstDriver.uProductIDorSubClass, 2, 3, 4, 5, 6);
|
|
|
|
USB2_MSE_VDBG("usb2MseInit(): uBCDUSBorProtocol : 0x%X :\n",
|
|
pUsb2MseDriver->hstDriver.uBCDUSBorProtocol, 2, 3, 4, 5, 6);
|
|
|
|
/* Register the client with USBD */
|
|
|
|
USB2_MSE_VDBG("usb2MseInit(): registering the client with USBD...\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
hstStatus = usbHstDriverRegister (&pUsb2MseDriver->hstDriver,
|
|
NULL
|
|
/* VxWorks 6.6 and above has a 3rd parameter specfying the name of the driver */
|
|
#if (_WRS_VXWORKS_MAJOR > 6) || \
|
|
((_WRS_VXWORKS_MAJOR == 6) && (_WRS_VXWORKS_MINOR >= 6))
|
|
,
|
|
|
|
"usbGen2Mouse"
|
|
#endif
|
|
);
|
|
|
|
if (hstStatus != USBHST_SUCCESS)
|
|
{
|
|
USB2_MSE_ERR("usb2MseInit(): cannot register with USBD...\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
iosDrvRemove(usb2MseDrvNum, TRUE);
|
|
usb2MseDrvNum = 0;
|
|
|
|
usb2ClassDriverDelete(pUsb2MseDriver);
|
|
pUsb2MseDriver = NULL;
|
|
|
|
return ERROR;
|
|
}
|
|
|
|
USB2_MSE_DBG("usb2MseInit(): Class driver registration succeeded.\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
usb2ReleaseDriver(pUsb2MseDriver);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* usb2MseDeinit - unregister the USB mouse class driver from the USBD
|
|
*
|
|
* This routine is to de-initialize the mouse driver from the USBD, and to free
|
|
* all related resources.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
void usb2MseDeinit
|
|
(
|
|
void
|
|
)
|
|
{
|
|
USBHST_STATUS hstStatus;
|
|
BOOL driverEmpty;
|
|
|
|
if (OK != usb2TakeDriver(pUsb2MseDriver))
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeinit(): Driver mutex not taken\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
return;
|
|
}
|
|
|
|
hstStatus = usbHstDriverDeregister(&(pUsb2MseDriver->hstDriver));
|
|
|
|
if (hstStatus != USBHST_SUCCESS)
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeinit(): "
|
|
"usbHstDriverDeregister failed %d\n",
|
|
hstStatus, 2, 3, 4, 5, 6);
|
|
|
|
usb2ReleaseDriver(pUsb2MseDriver);
|
|
|
|
return;
|
|
}
|
|
|
|
driverEmpty = usb2DriverIsEmpty (pUsb2MseDriver);
|
|
|
|
while (FALSE == driverEmpty)
|
|
{
|
|
usb2ReleaseDriver(pUsb2MseDriver);
|
|
|
|
taskDelay(sysClkRateGet());
|
|
|
|
if (OK != usb2TakeDriver(pUsb2MseDriver))
|
|
{
|
|
USB2_MSE_ERR("usb2MseDeinit(): Driver mutex not taken\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
return;
|
|
}
|
|
|
|
driverEmpty = usb2DriverIsEmpty (pUsb2MseDriver);
|
|
}
|
|
|
|
if (usb2MseDrvNum > 0)
|
|
{
|
|
iosDrvRemove(usb2MseDrvNum, FALSE);
|
|
usb2MseDrvNum = 0;
|
|
}
|
|
|
|
usb2ClassDriverDelete(pUsb2MseDriver);
|
|
|
|
pUsb2MseDriver = NULL;
|
|
|
|
USB2_MSE_DBG("usb2MseDeinit(): Class driver deregistration succeeded.\n",
|
|
1, 2, 3, 4, 5, 6);
|
|
|
|
return;
|
|
}
|
|
|