| /**************************************************************************** |
| * drivers/usbhost/usbhost_hidmouse.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <sys/types.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <poll.h> |
| #include <signal.h> |
| #include <fcntl.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <fixedmath.h> |
| #include <debug.h> |
| |
| #include <nuttx/irq.h> |
| #include <nuttx/kmalloc.h> |
| #include <nuttx/kthread.h> |
| #include <nuttx/fs/fs.h> |
| #include <nuttx/wqueue.h> |
| #include <nuttx/signal.h> |
| #include <nuttx/mutex.h> |
| #include <nuttx/semaphore.h> |
| |
| #include <nuttx/usb/usb.h> |
| #include <nuttx/usb/usbhost.h> |
| #include <nuttx/usb/hid.h> |
| #include <nuttx/usb/usbhost_devaddr.h> |
| |
| #ifdef CONFIG_HIDMOUSE_TSCIF |
| # include <nuttx/input/touchscreen.h> |
| #else |
| # include <nuttx/input/mouse.h> |
| #endif |
| |
| /* Don't compile if prerequisites are not met */ |
| |
| #if defined(CONFIG_USBHOST) && !defined(CONFIG_USBHOST_INT_DISABLE) |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* Configuration ************************************************************/ |
| |
| /* Worker thread is needed, unfortunately, to handle some cornercase failure |
| * conditions. This is kind of wasteful and begs for a re-design. |
| */ |
| |
| #ifndef CONFIG_SCHED_WORKQUEUE |
| # warning "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)" |
| #endif |
| |
| /* Provide some default values for other configuration settings */ |
| |
| #ifndef CONFIG_HIDMOUSE_XMAX |
| # define CONFIG_HIDMOUSE_XMAX 320 |
| #endif |
| |
| #define HIDMOUSE_XMAX_B16 (CONFIG_HIDMOUSE_XMAX << 16) |
| |
| #ifndef CONFIG_HIDMOUSE_YMAX |
| # define CONFIG_HIDMOUSE_YMAX 240 |
| #endif |
| |
| #define HIDMOUSE_YMAX_B16 (CONFIG_HIDMOUSE_YMAX << 16) |
| |
| #ifndef CONFIG_HIDMOUSE_XSCALE |
| # define CONFIG_HIDMOUSE_XSCALE 0x00010000 |
| #endif |
| |
| #ifndef CONFIG_HIDMOUSE_YSCALE |
| # define CONFIG_HIDMOUSE_YSCALE 0x00010000 |
| #endif |
| |
| #ifndef CONFIG_HIDMOUSE_XTHRESH |
| # define CONFIG_HIDMOUSE_XTHRESH 12 |
| #endif |
| |
| #define HIDMOUSE_XTHRESH_B16 (CONFIG_HIDMOUSE_XTHRESH << 16) |
| |
| #ifndef CONFIG_HIDMOUSE_YTHRESH |
| # define CONFIG_HIDMOUSE_YTHRESH 12 |
| #endif |
| |
| #define HIDMOUSE_YTHRESH_B16 (CONFIG_HIDMOUSE_YTHRESH << 16) |
| |
| #ifdef CONFIG_HIDMOUSE_TSCIF |
| # undef CONFIG_INPUT_MOUSE_WHEEL |
| #endif |
| |
| #ifdef CONFIG_INPUT_MOUSE_WHEEL |
| |
| # ifndef CONFIG_HIDMOUSE_WMAX |
| # define CONFIG_HIDMOUSE_WMAX 100 |
| # endif |
| |
| # define HIDMOUSE_WMAX_B16 (CONFIG_HIDMOUSE_WMAX << 16) |
| |
| # ifndef CONFIG_HIDMOUSE_WSCALE |
| # define CONFIG_HIDMOUSE_WSCALE 0x00010000 |
| # endif |
| |
| # ifndef CONFIG_HIDMOUSE_WTHRESH |
| # define CONFIG_HIDMOUSE_WTHRESH 1 |
| # endif |
| |
| # define HIDMOUSE_WTHRESH_B16 (CONFIG_HIDMOUSE_WTHRESH << 16) |
| |
| #endif /* CONFIG_INPUT_MOUSE_WHEEL */ |
| |
| #ifndef CONFIG_HIDMOUSE_DEFPRIO |
| # define CONFIG_HIDMOUSE_DEFPRIO 50 |
| #endif |
| |
| #ifndef CONFIG_HIDMOUSE_STACKSIZE |
| # define CONFIG_HIDMOUSE_STACKSIZE 1024 |
| #endif |
| |
| #ifndef CONFIG_HIDMOUSE_NPOLLWAITERS |
| # define CONFIG_HIDMOUSE_NPOLLWAITERS 2 |
| #endif |
| |
| /* This is a value for the threshold that guarantees a big difference on the |
| * first left button (but can't overflow). |
| */ |
| |
| #define INVALID_POSITION_B16 (0x7fffffff) |
| |
| /* Driver support ***********************************************************/ |
| |
| /* This format is used to construct the /dev/mouse[n] device driver path. It |
| * defined here so that it will be used consistently in all places. |
| */ |
| |
| #ifdef CONFIG_HIDMOUSE_TSCIF |
| # define DEV_FORMAT "/dev/input%d" |
| # define DEV_NAMELEN 14 |
| #else |
| # define DEV_FORMAT "/dev/mouse%d" |
| # define DEV_NAMELEN 14 |
| #endif |
| |
| /* Used in usbhost_cfgdesc() */ |
| |
| #define USBHOST_IFFOUND 0x01 /* Required I/F descriptor found */ |
| #define USBHOST_EPINFOUND 0x02 /* Required interrupt IN EP descriptor found */ |
| #define USBHOST_ALLFOUND (USBHOST_IFFOUND|USBHOST_EPINFOUND) |
| |
| #define USBHOST_MAX_CREFS 0x7fff |
| |
| /* Debug ********************************************************************/ |
| |
| /* Both CONFIG_DEBUG_INPUT and CONFIG_DEBUG_USB could apply to this file. |
| * We assume here that CONFIG_DEBUG_INPUT might be enabled separately, but |
| * CONFIG_DEBUG_USB implies both. |
| */ |
| |
| #ifndef CONFIG_DEBUG_INPUT |
| # undef ierr |
| # define ierr uerr |
| # undef iinfo |
| # define iinfo uinfo |
| #endif |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_HIDMOUSE_TSCIF |
| /* This describes the state of one event */ |
| |
| enum mouse_button_e |
| { |
| BUTTON_NONE = 0, /* No button pressed */ |
| BUTTON_PRESSED, /* Left buffer pressed */ |
| BUTTON_MOVE, /* Same button press, possibly different position */ |
| BUTTON_RELEASED /* Left button released */ |
| }; |
| |
| /* This structure describes the results of one mouse sample. Since this |
| * emulates a touchscreen, only the mouse left button is used. |
| */ |
| |
| struct mouse_sample_s |
| { |
| uint8_t id; /* Sampled touch point ID */ |
| uint8_t event; /* Button state (see enum mouse_button_e) */ |
| bool valid; /* True: x,y contain valid, sampled data */ |
| uint16_t x; /* Measured X position */ |
| uint16_t y; /* Measured Y position */ |
| }; |
| |
| #else |
| /* This structure summarizes the mouse report */ |
| |
| struct mouse_sample_s |
| { |
| uint8_t buttons; /* Button state (see MOUSE_BUTTON_* definitions) */ |
| uint16_t x; /* Accumulated X position */ |
| uint16_t y; /* Accumulated Y position */ |
| #ifdef CONFIG_INPUT_MOUSE_WHEEL |
| uint16_t wheel; /* Reported wheel position */ |
| #endif |
| }; |
| #endif |
| |
| /* This structure contains the internal, private state of the USB host |
| * mouse storage class. |
| */ |
| |
| struct usbhost_state_s |
| { |
| /* This is the externally visible portion of the state */ |
| |
| struct usbhost_class_s usbclass; |
| |
| /* This is an instance of the USB host driver |
| * bound to this class instance |
| */ |
| |
| struct usbhost_driver_s *drvr; |
| |
| /* The remainder of the fields are provide o the mouse class driver */ |
| |
| volatile bool disconnected; /* TRUE: Device has been disconnected */ |
| volatile bool polling; /* TRUE: Poll thread is running */ |
| volatile bool open; /* TRUE: The mouse device is open */ |
| volatile bool valid; /* TRUE: New sample data is available */ |
| uint8_t devno; /* Minor number in the /dev/mouse[n] device */ |
| uint8_t nwaiters; /* Number of threads waiting for mouse data */ |
| #ifdef CONFIG_HIDMOUSE_TSCIF |
| uint8_t id; /* Current "touch" point ID */ |
| #else |
| uint8_t buttons; /* Current state of the mouse buttons */ |
| #endif |
| int16_t crefs; /* Reference count on the driver instance */ |
| mutex_t lock; /* Used to maintain mutual exclusive access */ |
| sem_t waitsem; /* Used to wait for mouse data */ |
| FAR uint8_t *tbuffer; /* The allocated transfer buffer */ |
| b16_t xaccum; /* Current integrated X position */ |
| b16_t yaccum; /* Current integrated Y position */ |
| b16_t xlast; /* Last reported X position */ |
| b16_t ylast; /* Last reported Y position */ |
| #ifdef CONFIG_INPUT_MOUSE_WHEEL |
| b16_t waccum; /* Current integrated while position */ |
| b16_t wlast; /* Last reported wheel position */ |
| #endif |
| size_t tbuflen; /* Size of the allocated transfer buffer */ |
| pid_t pollpid; /* PID of the poll task */ |
| struct work_s work; /* For cornercase error handling by the worker thread */ |
| struct mouse_sample_s sample; /* Last sampled mouse data */ |
| usbhost_ep_t epin; /* Interrupt IN endpoint */ |
| |
| /* The following is a list if poll structures of threads waiting for |
| * driver events. The 'struct pollfd' reference for each open is also |
| * retained in the f_priv field of the 'struct file'. |
| */ |
| |
| struct pollfd *fds[CONFIG_HIDMOUSE_NPOLLWAITERS]; |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| /* Memory allocation services */ |
| |
| static inline FAR struct usbhost_state_s *usbhost_allocclass(void); |
| static inline void usbhost_freeclass(FAR struct usbhost_state_s *usbclass); |
| |
| /* Device name management */ |
| |
| static int usbhost_allocdevno(FAR struct usbhost_state_s *priv); |
| static void usbhost_freedevno(FAR struct usbhost_state_s *priv); |
| static inline void usbhost_mkdevname(FAR struct usbhost_state_s *priv, |
| FAR char *devname); |
| |
| /* Mouse polling thread */ |
| |
| static void usbhost_destroy(FAR void *arg); |
| static void usbhost_notify(FAR struct usbhost_state_s *priv); |
| static void usbhost_position(FAR struct usbhost_state_s *priv, |
| FAR struct usbhid_mousereport_s *rpt); |
| #ifdef CONFIG_HIDMOUSE_TSCIF |
| static bool usbhost_touchscreen(FAR struct usbhost_state_s *priv, |
| FAR struct usbhid_mousereport_s *rpt); |
| #endif |
| static bool usbhost_threshold(FAR struct usbhost_state_s *priv); |
| static int usbhost_mouse_poll(int argc, char *argv[]); |
| static int usbhost_sample(FAR struct usbhost_state_s *priv, |
| FAR struct mouse_sample_s *sample); |
| static int usbhost_waitsample(FAR struct usbhost_state_s *priv, |
| FAR struct mouse_sample_s *sample); |
| |
| /* Helpers for usbhost_connect() */ |
| |
| static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv, |
| FAR const uint8_t *configdesc, |
| int desclen); |
| static inline int usbhost_devinit(FAR struct usbhost_state_s *priv); |
| |
| /* (Little Endian) Data helpers */ |
| |
| static inline uint16_t usbhost_getle16(const uint8_t *val); |
| |
| /* Transfer descriptor memory management */ |
| |
| static inline int usbhost_tdalloc(FAR struct usbhost_state_s *priv); |
| static inline int usbhost_tdfree(FAR struct usbhost_state_s *priv); |
| |
| /* struct usbhost_registry_s methods */ |
| |
| static FAR struct usbhost_class_s * |
| usbhost_create(FAR struct usbhost_hubport_s *hport, |
| FAR const struct usbhost_id_s *id); |
| |
| /* struct usbhost_class_s methods */ |
| |
| static int usbhost_connect(FAR struct usbhost_class_s *usbclass, |
| FAR const uint8_t *configdesc, int desclen); |
| static int usbhost_disconnected(FAR struct usbhost_class_s *usbclass); |
| |
| /* Driver methods. We export the mouse as a standard character driver */ |
| |
| static int usbhost_open(FAR struct file *filep); |
| static int usbhost_close(FAR struct file *filep); |
| static ssize_t usbhost_read(FAR struct file *filep, |
| FAR char *buffer, size_t len); |
| static ssize_t usbhost_write(FAR struct file *filep, |
| FAR const char *buffer, size_t len); |
| static int usbhost_poll(FAR struct file *filep, FAR struct pollfd *fds, |
| bool setup); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* This structure provides the registry entry ID information that will be |
| * used to associate the USB host mouse class driver to a connected USB |
| * device. |
| */ |
| |
| static const struct usbhost_id_s g_hidmouse_id = |
| { |
| USB_CLASS_HID, /* base */ |
| USBHID_SUBCLASS_BOOTIF, /* subclass */ |
| USBHID_PROTOCOL_MOUSE, /* proto */ |
| 0, /* vid */ |
| 0 /* pid */ |
| }; |
| |
| /* This is the USB host storage class's registry entry */ |
| |
| static struct usbhost_registry_s g_hidmouse = |
| { |
| NULL, /* flink */ |
| usbhost_create, /* create */ |
| 1, /* nids */ |
| &g_hidmouse_id /* id[] */ |
| }; |
| |
| static const struct file_operations g_hidmouse_fops = |
| { |
| usbhost_open, /* open */ |
| usbhost_close, /* close */ |
| usbhost_read, /* read */ |
| usbhost_write, /* write */ |
| NULL, /* seek */ |
| NULL, /* ioctl */ |
| NULL, /* mmap */ |
| NULL, /* truncate */ |
| usbhost_poll /* poll */ |
| }; |
| |
| /* This is a bitmap that is used to allocate device names /dev/mouse0-31. */ |
| |
| static uint32_t g_devinuse; |
| |
| /* The following are used to managed the class creation operation */ |
| |
| static mutex_t g_lock = NXMUTEX_INITIALIZER; |
| static sem_t g_syncsem = SEM_INITIALIZER(0); |
| static FAR struct usbhost_state_s *g_priv; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: usbhost_allocclass |
| * |
| * Description: |
| * This is really part of the logic that implements the create() method |
| * of struct usbhost_registry_s. This function allocates memory for one |
| * new class instance. |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * On success, this function will return a non-NULL instance of struct |
| * usbhost_class_s. NULL is returned on failure; this function will |
| * will fail only if there are insufficient resources to create another |
| * USB host class instance. |
| * |
| ****************************************************************************/ |
| |
| static inline FAR struct usbhost_state_s *usbhost_allocclass(void) |
| { |
| FAR struct usbhost_state_s *priv; |
| |
| DEBUGASSERT(!up_interrupt_context()); |
| |
| priv = (FAR struct usbhost_state_s *) |
| kmm_malloc(sizeof(struct usbhost_state_s)); |
| |
| uinfo("Allocated: %p\n", priv); |
| return priv; |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_freeclass |
| * |
| * Description: |
| * Free a class instance previously allocated by usbhost_allocclass(). |
| * |
| * Input Parameters: |
| * usbclass - A reference to the class instance to be freed. |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static inline void usbhost_freeclass(FAR struct usbhost_state_s *usbclass) |
| { |
| DEBUGASSERT(usbclass != NULL); |
| |
| /* Free the class instance. */ |
| |
| uinfo("Freeing: %p\n", usbclass); |
| kmm_free(usbclass); |
| } |
| |
| /**************************************************************************** |
| * Name: Device name management |
| * |
| * Description: |
| * Some tiny functions to coordinate management of device names. |
| * |
| ****************************************************************************/ |
| |
| static int usbhost_allocdevno(FAR struct usbhost_state_s *priv) |
| { |
| irqstate_t flags; |
| int devno; |
| |
| flags = enter_critical_section(); |
| for (devno = 0; devno < 26; devno++) |
| { |
| uint32_t bitno = 1 << devno; |
| if ((g_devinuse & bitno) == 0) |
| { |
| g_devinuse |= bitno; |
| priv->devno = devno; |
| leave_critical_section(flags); |
| return OK; |
| } |
| } |
| |
| leave_critical_section(flags); |
| return -EMFILE; |
| } |
| |
| static void usbhost_freedevno(FAR struct usbhost_state_s *priv) |
| { |
| int devno = priv->devno; |
| |
| if (devno >= 0 && devno < 26) |
| { |
| irqstate_t flags = enter_critical_section(); |
| g_devinuse &= ~(1 << devno); |
| leave_critical_section(flags); |
| } |
| } |
| |
| static inline void usbhost_mkdevname(FAR struct usbhost_state_s *priv, |
| FAR char *devname) |
| { |
| snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->devno); |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_destroy |
| * |
| * Description: |
| * The USB device has been disconnected and the reference count on the USB |
| * host class instance has gone to 1.. Time to destroy the USB host class |
| * instance. |
| * |
| * Input Parameters: |
| * arg - A reference to the class instance to be destroyed. |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void usbhost_destroy(FAR void *arg) |
| { |
| FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)arg; |
| FAR struct usbhost_hubport_s *hport; |
| char devname[DEV_NAMELEN]; |
| |
| DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL); |
| uinfo("crefs: %d\n", priv->crefs); |
| |
| hport = priv->usbclass.hport; |
| |
| /* Unregister the driver */ |
| |
| uinfo("Unregister driver\n"); |
| usbhost_mkdevname(priv, devname); |
| unregister_driver(devname); |
| |
| /* Release the device name used by this connection */ |
| |
| usbhost_freedevno(priv); |
| |
| /* Free the interrupt endpoints */ |
| |
| if (priv->epin) |
| { |
| DRVR_EPFREE(hport->drvr, priv->epin); |
| } |
| |
| /* Free any transfer buffers */ |
| |
| usbhost_tdfree(priv); |
| |
| /* Destroy the mutex & semaphores */ |
| |
| nxmutex_destroy(&priv->lock); |
| nxsem_destroy(&priv->waitsem); |
| |
| /* Disconnect the USB host device */ |
| |
| DRVR_DISCONNECT(hport->drvr, hport); |
| |
| /* Free the function address assigned to this device */ |
| |
| usbhost_devaddr_destroy(hport, hport->funcaddr); |
| hport->funcaddr = 0; |
| |
| /* And free the class instance. */ |
| |
| usbhost_freeclass(priv); |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_notify |
| * |
| * Description: |
| * Wake any threads waiting for mouse data |
| * |
| * Input Parameters: |
| * priv - A reference to the mouse state structure. |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void usbhost_notify(FAR struct usbhost_state_s *priv) |
| { |
| /* If there are threads waiting for read data, then signal one of them |
| * that the read data is available. |
| */ |
| |
| if (priv->nwaiters > 0) |
| { |
| nxsem_post(&priv->waitsem); |
| } |
| |
| /* If there are threads waiting on poll() for mouse data to become |
| * available, then wake them up now. NOTE: we wake up all waiting |
| * threads because we do not know that they are going to do. If they |
| * all try to read the data, then some make end up blocking after all. |
| */ |
| |
| poll_notify(priv->fds, CONFIG_HIDMOUSE_NPOLLWAITERS, POLLIN); |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_position |
| * |
| * Description: |
| * Integrate the current mouse displacement to get the updated mouse |
| * position. |
| * |
| * Input Parameters: |
| * priv - A reference to the mouse state structure. |
| * rpt - The new mouse report data. |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| static void usbhost_position(FAR struct usbhost_state_s *priv, |
| FAR struct usbhid_mousereport_s *rpt) |
| { |
| int32_t disp; |
| b16_t pos; |
| |
| /* The following logic performs an constant integration of the mouse X/Y |
| * displacement data in order to keep the X/Y positional data current. |
| */ |
| |
| /* Sign extend the mouse X position. We do this manually because some |
| * architectures do not support signed character types and some compilers |
| * may be configured to treat all characters as unsigned. |
| */ |
| |
| #ifdef CONFIG_HIDMOUSE_SWAPXY |
| disp = rpt->ydisp; |
| if ((rpt->ydisp & 0x80) != 0) |
| { |
| disp |= 0xffffff00; |
| } |
| #else |
| disp = rpt->xdisp; |
| if ((rpt->xdisp & 0x80) != 0) |
| { |
| disp |= 0xffffff00; |
| } |
| #endif |
| |
| /* Scale the X displacement and determine the new X position */ |
| |
| pos = priv->xaccum + CONFIG_HIDMOUSE_XSCALE * disp; |
| |
| /* Make sure that the scaled X position does not become negative or exceed |
| * the maximum. |
| */ |
| |
| if (pos > HIDMOUSE_XMAX_B16) |
| { |
| pos = HIDMOUSE_XMAX_B16; |
| } |
| else if (pos < 0) |
| { |
| pos = 0; |
| } |
| |
| /* Save the updated X position */ |
| |
| priv->xaccum = pos; |
| |
| /* Do the same for the Y position */ |
| |
| #ifdef CONFIG_HIDMOUSE_SWAPXY |
| disp = rpt->xdisp; |
| if ((rpt->xdisp & 0x80) != 0) |
| { |
| disp |= 0xffffff00; |
| } |
| #else |
| disp = rpt->ydisp; |
| if ((rpt->ydisp & 0x80) != 0) |
| { |
| disp |= 0xffffff00; |
| } |
| #endif |
| |
| pos = priv->yaccum + CONFIG_HIDMOUSE_YSCALE * disp; |
| |
| if (pos > HIDMOUSE_YMAX_B16) |
| { |
| pos = HIDMOUSE_YMAX_B16; |
| } |
| else if (pos < 0) |
| { |
| pos = 0; |
| } |
| |
| priv->yaccum = pos; |
| |
| #ifdef CONFIG_INPUT_MOUSE_WHEEL |
| /* Do the same for the wheel position */ |
| |
| disp = rpt->wdisp; |
| if ((rpt->wdisp & 0x80) != 0) |
| { |
| disp |= 0xffffff00; |
| } |
| |
| pos = priv->waccum + CONFIG_HIDMOUSE_WSCALE * disp; |
| |
| if (pos > HIDMOUSE_WMAX_B16) |
| { |
| pos = HIDMOUSE_WMAX_B16; |
| } |
| else if (pos < 0) |
| { |
| pos = 0; |
| } |
| |
| priv->waccum = pos; |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_touchscreen |
| * |
| * Description: |
| * Execute the (emulated) touchscreen press/drag/release state machine. |
| * |
| * Input Parameters: |
| * priv - A reference to the mouse state structure. |
| * rpt - The new mouse report data. |
| * |
| * Returned Value: |
| * False if the mouse data should not be reported. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_HIDMOUSE_TSCIF |
| static bool usbhost_touchscreen(FAR struct usbhost_state_s *priv, |
| FAR struct usbhid_mousereport_s *rpt) |
| { |
| /* Check if the left button is pressed */ |
| |
| if ((rpt->buttons & USBHID_MOUSEIN_BUTTON1) == 0) |
| { |
| /* The left button is not pressed.. reset thresholding variables. */ |
| |
| priv->xlast = INVALID_POSITION_B16; |
| priv->ylast = INVALID_POSITION_B16; |
| |
| /* Ignore the report if the button was not pressed last time |
| * (BUTTON_NONE == button released and already reported; |
| * BUTTON_RELEASED == button released, but not yet reported) |
| */ |
| |
| if (priv->sample.event == BUTTON_NONE || |
| priv->sample.event == BUTTON_RELEASED) |
| { |
| return false; |
| } |
| |
| /* The left button has just been released. NOTE: We know from a |
| * previous test, that this is a button release condition. This will |
| * be changed to BUTTON_NONE after the button release has been |
| * reported. |
| */ |
| |
| priv->sample.event = BUTTON_RELEASED; |
| } |
| |
| /* It is a left button press event. If the last button release event has |
| * not been processed yet, then we have to ignore the button press event |
| * (or else it will look like a drag event) |
| */ |
| |
| else if (priv->sample.event == BUTTON_RELEASED) |
| { |
| /* If we have not yet processed the button release event, then we |
| * cannot handle this button press event. We will have to discard the |
| * data and wait for the next sample. |
| */ |
| |
| return false; |
| } |
| |
| /* Handle left-button down events */ |
| |
| else |
| { |
| /* If this is the first left button press report, then report that |
| * event. If event == BUTTON_PRESSED, it will be set to set to |
| * BUTTON_MOVE after the button press is first sampled. |
| */ |
| |
| if (priv->sample.event != BUTTON_MOVE) |
| { |
| /* First button press */ |
| |
| priv->sample.event = BUTTON_PRESSED; |
| } |
| |
| /* Otherwise, perform a thresholding operation so that the results |
| * will be more stable. If the difference from the last sample is |
| * small, then ignore the event. |
| */ |
| |
| else if (!usbhost_xythreshold(priv)) |
| { |
| return false; |
| } |
| } |
| |
| /* We get here: |
| * |
| * (1) When the left button is just release, |
| * (2) When the left button is first pressed, or |
| * (3) When the left button is held and some significant 'dragging' |
| * has occurred. |
| */ |
| |
| return true; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: usbhost_threshold |
| * |
| * Description: |
| * Check if the current mouse position differs from the previous mouse |
| * position by a threshold amount. |
| * |
| * Input Parameters: |
| * priv - A reference to the mouse state structure. |
| * |
| * Returned Value: |
| * True if the mouse position is significantly different from the last |
| * reported mouse position. |
| * |
| ****************************************************************************/ |
| |
| static bool usbhost_threshold(FAR struct usbhost_state_s *priv) |
| { |
| #if CONFIG_HIDMOUSE_XTHRESH > 0 && CONFIG_HIDMOUSE_YTHRESH > 0 |
| b16_t pos; |
| b16_t diff; |
| |
| /* Get the difference in the X position from the last report */ |
| |
| pos = priv->xaccum; |
| if (pos > priv->xlast) |
| { |
| diff = pos - priv->xlast; |
| } |
| else |
| { |
| diff = priv->xlast - pos; |
| } |
| |
| /* Check if the X difference exceeds the report threshold */ |
| |
| if (diff >= HIDMOUSE_XTHRESH_B16) |
| { |
| return true; |
| } |
| |
| /* Little or no change in the X direction, check the Y direction. */ |
| |
| pos = priv->yaccum; |
| if (pos > priv->ylast) |
| { |
| diff = pos - priv->ylast; |
| } |
| else |
| { |
| diff = priv->ylast - pos; |
| } |
| |
| if (diff >= HIDMOUSE_YTHRESH_B16) |
| { |
| return true; |
| } |
| |
| #ifdef CONFIG_INPUT_MOUSE_WHEEL |
| /* Get the difference in the wheel position from the last report */ |
| |
| pos = priv->waccum; |
| if (pos > priv->wlast) |
| { |
| diff = pos - priv->wlast; |
| } |
| else |
| { |
| diff = priv->wlast - pos; |
| } |
| |
| /* Check if the X difference exceeds the report threshold */ |
| |
| if (diff >= HIDMOUSE_WTHRESH_B16) |
| { |
| return true; |
| } |
| #endif |
| |
| /* Little or no change in either direction... don't report anything. */ |
| |
| return false; |
| #else |
| /* No thresholding */ |
| |
| return true; |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_mouse_poll |
| * |
| * Description: |
| * Periodically check for new mouse data. |
| * |
| * Input Parameters: |
| * arg - A reference to the class instance to be destroyed. |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static int usbhost_mouse_poll(int argc, char *argv[]) |
| { |
| FAR struct usbhost_state_s *priv; |
| FAR struct usbhost_hubport_s *hport; |
| FAR struct usbhid_mousereport_s *rpt; |
| irqstate_t flags; |
| #ifndef CONFIG_HIDMOUSE_TSCIF |
| uint8_t buttons; |
| #endif |
| #if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_INFO) |
| unsigned int npolls = 0; |
| #endif |
| unsigned int nerrors = 0; |
| ssize_t nbytes; |
| int ret = OK; |
| |
| uinfo("Started\n"); |
| |
| /* Synchronize with the start-up logic. Get the private instance, re-start |
| * the start-up logic, and wait a bit to make sure that all of the class |
| * creation logic has a chance to run to completion. |
| * |
| * NOTE: that the reference count is *not* incremented here. When the |
| * driver structure was created, it was created with a reference count of |
| * one. This thread is responsible for that count. The count will be |
| * decremented when this thread exits. |
| */ |
| |
| priv = g_priv; |
| DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL); |
| hport = priv->usbclass.hport; |
| |
| priv->polling = true; |
| nxsem_post(&g_syncsem); |
| nxsig_sleep(1); |
| |
| /* Loop here until the device is disconnected */ |
| |
| uinfo("Entering poll loop\n"); |
| |
| while (!priv->disconnected) |
| { |
| /* Read the next mouse report. We will stall here until the mouse |
| * sends data. |
| */ |
| |
| nbytes = DRVR_TRANSFER(hport->drvr, priv->epin, |
| priv->tbuffer, priv->tbuflen); |
| |
| /* Check for errors -- Bail if an excessive number of consecutive |
| * errors are encountered. |
| */ |
| |
| if (nbytes < 0) |
| { |
| /* If DRVR_TRANSFER() returns EAGAIN, that simply means that |
| * the devices was not ready and has NAK'ed the transfer. That |
| * should not be treated as an error (unless it persists for a |
| * long time). |
| */ |
| |
| uerr("ERROR: DRVR_TRANSFER returned: %d/%u\n", |
| (int)nbytes, nerrors); |
| |
| if (nbytes != -EAGAIN) |
| { |
| if (++nerrors > 200) |
| { |
| uerr(" Too many errors... aborting: %d\n", nerrors); |
| ret = (int)nbytes; |
| break; |
| } |
| } |
| } |
| |
| /* The report was received correctly. */ |
| |
| else |
| { |
| /* Success, reset the error counter */ |
| |
| nerrors = 0; |
| |
| /* Ignore the mouse data if no task has opened the driver. */ |
| |
| if (priv->open) |
| { |
| /* Get exclusive access to the mouse state data */ |
| |
| ret = nxmutex_lock(&priv->lock); |
| if (ret < 0) |
| { |
| /* Break out and disconnect if the thread is canceled. */ |
| |
| break; |
| } |
| |
| /* Get the HID mouse report */ |
| |
| rpt = (FAR struct usbhid_mousereport_s *)priv->tbuffer; |
| |
| /* Get the updated mouse position */ |
| |
| usbhost_position(priv, rpt); |
| |
| #ifdef CONFIG_HIDMOUSE_TSCIF |
| /* Execute the touchscreen state machine */ |
| |
| if (usbhost_touchscreen(priv, rpt)) |
| #else |
| /* Check if any buttons have changed. If so, then report the |
| * new mouse data. |
| * |
| * If not, then perform a thresholding operation so that the |
| * results will be more stable. If the difference from the |
| * last sample is small, then ignore the event. |
| */ |
| |
| buttons = rpt->buttons & USBHID_MOUSEIN_BUTTON_MASK; |
| if (buttons != priv->buttons || usbhost_threshold(priv)) |
| #endif |
| { |
| /* We get here when either there is a meaningful button |
| * change and/or a significant movement of the mouse. We |
| * are going to report the mouse event. |
| * |
| * Snap to the new x/y position for subsequent |
| * thresholding |
| */ |
| |
| priv->xlast = priv->xaccum; |
| priv->ylast = priv->yaccum; |
| #ifdef CONFIG_INPUT_MOUSE_WHEEL |
| priv->wlast = priv->waccum; |
| #endif |
| /* Update the sample X/Y positions */ |
| |
| priv->sample.x = b16toi(priv->xaccum); |
| priv->sample.y = b16toi(priv->yaccum); |
| #ifdef CONFIG_INPUT_MOUSE_WHEEL |
| priv->sample.wheel = b16toi(priv->waccum); |
| #endif |
| |
| #ifdef CONFIG_HIDMOUSE_TSCIF |
| /* The X/Y positional data is now valid */ |
| |
| priv->sample.valid = true; |
| |
| /* Indicate the availability of new sample data |
| * for this ID |
| */ |
| |
| priv->sample.id = priv->id; |
| #else |
| /* Report and remember the new button state */ |
| |
| priv->sample.buttons = buttons; |
| priv->buttons = buttons; |
| #endif |
| priv->valid = true; |
| |
| /* Notify any waiters that new HIDMOUSE data is available */ |
| |
| usbhost_notify(priv); |
| } |
| |
| /* Release our lock on the state structure */ |
| |
| nxmutex_unlock(&priv->lock); |
| } |
| } |
| |
| /* If USB debug is on, then provide some periodic indication that |
| * polling is still happening. |
| */ |
| |
| #if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_INFO) |
| npolls++; |
| if ((npolls & 31) == 0) |
| { |
| uinfo("Still polling: %d\n", npolls); |
| } |
| #endif |
| } |
| |
| /* We get here when the driver is removed, when too many errors have |
| * been encountered, or the parent thread has been canceled. |
| * |
| * Make sure that we have exclusive access to the private data structure. |
| * There may now be other tasks with the character driver open and actively |
| * trying to interact with the class driver. |
| */ |
| |
| nxmutex_lock(&priv->lock); |
| |
| /* Indicate that we are no longer running and decrement the reference |
| * count held by this thread. If there are no other users of the class, |
| * we can destroy it now. Otherwise, we have to wait until the all |
| * of the file descriptors are closed. |
| */ |
| |
| uinfo("Mouse removed, polling halted\n"); |
| |
| flags = enter_critical_section(); |
| priv->polling = false; |
| |
| /* Decrement the reference count held by this thread. */ |
| |
| DEBUGASSERT(priv->crefs > 0); |
| priv->crefs--; |
| |
| /* There are two possibilities: |
| * 1) The reference count is greater than zero. This means that there |
| * are still open references to the mouse driver. In this case |
| * we need to wait until usbhost_close() is called and all of the |
| * open driver references are decremented. Then usbhost_destroy() can |
| * be called from usbhost_close(). |
| * 2) The reference count is now zero. This means that there are no |
| * further open references and we can call usbhost_destroy() now. |
| */ |
| |
| if (priv->crefs < 1) |
| { |
| /* Unregister the driver and destroy the instance (while we hold |
| * the semaphore!) |
| */ |
| |
| usbhost_destroy(priv); |
| } |
| else |
| { |
| /* No, we will destroy the driver instance when it is final open |
| * reference is closed |
| */ |
| |
| nxmutex_unlock(&priv->lock); |
| } |
| |
| leave_critical_section(flags); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_sample |
| * |
| * Description: |
| * Check if mouse data is available |
| * |
| ****************************************************************************/ |
| |
| static int usbhost_sample(FAR struct usbhost_state_s *priv, |
| FAR struct mouse_sample_s *sample) |
| { |
| irqstate_t flags; |
| int ret = -EAGAIN; |
| |
| /* Interrupts must be disabled when this is called to (1) prevent posting |
| * of semaphores from interrupt handlers, and (2) to prevent sampled data |
| * from changing until it has been reported. |
| */ |
| |
| flags = enter_critical_section(); |
| |
| /* Is there new mouse data available? */ |
| |
| if (priv->valid) |
| { |
| /* Return a copy of the sampled data. */ |
| |
| memcpy(sample, &priv->sample, sizeof(struct mouse_sample_s)); |
| |
| #ifdef CONFIG_HIDMOUSE_TSCIF |
| /* Now manage state transitions */ |
| |
| if (sample->event == BUTTON_RELEASED) |
| { |
| /* Next.. No button press. Increment the ID so that next event ID |
| * will be unique. X/Y positions are no longer valid. |
| */ |
| |
| priv->sample.event = BUTTON_NONE; |
| priv->id++; |
| priv->sample.valid = false; |
| } |
| else if (sample->event == BUTTON_PRESSED) |
| { |
| /* First report -- next report will be a movement */ |
| |
| priv->sample.event = BUTTON_MOVE; |
| } |
| |
| #endif |
| |
| /* The sample has been reported and is no longer valid */ |
| |
| priv->valid = false; |
| ret = OK; |
| } |
| |
| leave_critical_section(flags); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_waitsample |
| * |
| * Description: |
| * Wait for the next valid mouse sample |
| * |
| * Input Parameters: |
| * priv - HID mouse state instance |
| * sample - The location to regurn the sample data |
| * |
| ****************************************************************************/ |
| |
| static int usbhost_waitsample(FAR struct usbhost_state_s *priv, |
| FAR struct mouse_sample_s *sample) |
| { |
| irqstate_t flags; |
| int ret; |
| |
| /* Interrupts must be disabled when this is called to (1) prevent posting |
| * of semaphores from interrupt handlers, and (2) to prevent sampled data |
| * from changing until it has been reported. |
| */ |
| |
| flags = enter_critical_section(); |
| |
| /* Now release the semaphore that manages mutually exclusive access to |
| * the device structure. This may cause other tasks to become ready to |
| * run, but they cannot run yet because pre-emption is disabled. |
| */ |
| |
| nxmutex_unlock(&priv->lock); |
| |
| /* Try to get the a sample... if we cannot, then wait on the semaphore |
| * that is posted when new sample data is available. |
| */ |
| |
| while (usbhost_sample(priv, sample) < 0) |
| { |
| /* Wait for a change in the HIDMOUSE state */ |
| |
| iinfo("Waiting..\n"); |
| priv->nwaiters++; |
| ret = nxsem_wait(&priv->waitsem); |
| priv->nwaiters--; |
| |
| if (ret < 0) |
| { |
| ierr("ERROR: nxsem_wait: %d\n", ret); |
| goto errout; |
| } |
| |
| /* Did the mouse become disconnected while we were waiting */ |
| |
| if (priv->disconnected) |
| { |
| ret = -ENODEV; |
| goto errout; |
| } |
| } |
| |
| iinfo("Sampled\n"); |
| |
| /* Re-acquire the semaphore that manages mutually exclusive access to |
| * the device structure. We may have to wait here. But we have our |
| * sample. Interrupts and pre-emption will be re-enabled while we wait. |
| */ |
| |
| ret = nxmutex_lock(&priv->lock); |
| |
| errout: |
| /* Then re-enable interrupts. We might get interrupt here and there |
| * could be a new sample. But no new threads will run because we still |
| * have pre-emption disabled. |
| */ |
| |
| leave_critical_section(flags); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_cfgdesc |
| * |
| * Description: |
| * This function implements the connect() method of struct |
| * usbhost_class_s. This method is a callback into the class |
| * implementation. It is used to provide the device's configuration |
| * descriptor to the class so that the class may initialize properly |
| * |
| * Input Parameters: |
| * priv - The USB host class instance. |
| * configdesc - A pointer to a uint8_t buffer container the configuration |
| * descriptor. |
| * desclen - The length in bytes of the configuration descriptor. |
| * |
| * Returned Value: |
| * On success, zero (OK) is returned. On a failure, a negated errno value |
| * is returned indicating the nature of the failure |
| * |
| * Assumptions: |
| * This function will *not* be called from an interrupt handler. |
| * |
| ****************************************************************************/ |
| |
| static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv, |
| FAR const uint8_t *configdesc, int desclen) |
| { |
| FAR struct usbhost_hubport_s *hport; |
| FAR struct usb_cfgdesc_s *cfgdesc; |
| FAR struct usb_desc_s *desc; |
| FAR struct usbhost_epdesc_s epindesc; |
| int remaining; |
| uint8_t found = 0; |
| bool done = false; |
| int ret; |
| |
| DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL && |
| configdesc != NULL && desclen >= sizeof(struct usb_cfgdesc_s)); |
| hport = priv->usbclass.hport; |
| |
| /* Keep the compiler from complaining about uninitialized variables */ |
| |
| memset(&epindesc, 0, sizeof(struct usbhost_epdesc_s)); |
| |
| /* Verify that we were passed a configuration descriptor */ |
| |
| cfgdesc = (FAR struct usb_cfgdesc_s *)configdesc; |
| if (cfgdesc->type != USB_DESC_TYPE_CONFIG) |
| { |
| return -EINVAL; |
| } |
| |
| /* Get the total length of the configuration descriptor (little endian). |
| * It might be a good check to get the number of interfaces here too. |
| */ |
| |
| remaining = (int)usbhost_getle16(cfgdesc->totallen); |
| |
| /* Skip to the next entry descriptor */ |
| |
| configdesc += cfgdesc->len; |
| remaining -= cfgdesc->len; |
| |
| /* Loop where there are more descriptors to examine */ |
| |
| while (remaining >= sizeof(struct usb_desc_s) && !done) |
| { |
| /* What is the next descriptor? */ |
| |
| desc = (FAR struct usb_desc_s *)configdesc; |
| switch (desc->type) |
| { |
| /* Interface descriptor. We really should get the number of endpoints |
| * from this descriptor too. |
| */ |
| |
| case USB_DESC_TYPE_INTERFACE: |
| { |
| uinfo("Interface descriptor\n"); |
| DEBUGASSERT(remaining >= USB_SIZEOF_IFDESC); |
| |
| /* Did we already find what we needed from |
| * a preceding interface? |
| */ |
| |
| if ((found & USBHOST_ALLFOUND) == USBHOST_ALLFOUND) |
| { |
| /* Yes.. then break out of the loop and use the preceding |
| * interface. |
| */ |
| |
| done = true; |
| } |
| else |
| { |
| /* Otherwise, discard any endpoints previously found */ |
| |
| found = USBHOST_IFFOUND; |
| } |
| } |
| break; |
| |
| /* HID descriptor */ |
| |
| case USBHID_DESCTYPE_HID: |
| uinfo("HID descriptor\n"); |
| break; |
| |
| /* Endpoint descriptor. We expect one or two interrupt endpoints, |
| * a required IN endpoint and an optional OUT endpoint. |
| */ |
| |
| case USB_DESC_TYPE_ENDPOINT: |
| { |
| FAR struct usb_epdesc_s *epdesc = |
| (FAR struct usb_epdesc_s *)configdesc; |
| |
| uinfo("Endpoint descriptor\n"); |
| DEBUGASSERT(remaining >= USB_SIZEOF_EPDESC); |
| |
| /* Check for an interrupt endpoint. */ |
| |
| if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) == |
| USB_EP_ATTR_XFER_INT) |
| { |
| /* Yes.. it is a interrupt endpoint. IN or OUT? */ |
| |
| if (USB_ISEPIN(epdesc->addr)) |
| { |
| /* It is an interrupt IN endpoint. There should be only |
| * one interrupt IN endpoint. |
| */ |
| |
| if ((found & USBHOST_EPINFOUND) != 0) |
| { |
| /* Oops.. more than one endpoint. We don't know what |
| * to do with this. |
| */ |
| |
| return -EINVAL; |
| } |
| |
| found |= USBHOST_EPINFOUND; |
| |
| /* Save the interrupt IN endpoint information */ |
| |
| epindesc.hport = hport; |
| epindesc.addr = epdesc->addr & |
| USB_EP_ADDR_NUMBER_MASK; |
| epindesc.in = true; |
| epindesc.xfrtype = USB_EP_ATTR_XFER_INT; |
| epindesc.interval = epdesc->interval; |
| epindesc.mxpacketsize = |
| usbhost_getle16(epdesc->mxpacketsize); |
| |
| uinfo("Interrupt IN EP addr:%d mxpacketsize:%d\n", |
| epindesc.addr, epindesc.mxpacketsize); |
| } |
| } |
| } |
| break; |
| |
| /* Other descriptors are just ignored for now */ |
| |
| default: |
| uinfo("Other descriptor: %d\n", desc->type); |
| break; |
| } |
| |
| /* What we found everything that we are going to find? */ |
| |
| if (found == USBHOST_ALLFOUND) |
| { |
| /* Yes.. then break out of the loop |
| * and use the preceding interface |
| */ |
| |
| done = true; |
| } |
| |
| /* Increment the address of the next descriptor */ |
| |
| configdesc += desc->len; |
| remaining -= desc->len; |
| } |
| |
| /* Sanity checking... did we find all of things that we need? */ |
| |
| if ((found & USBHOST_ALLFOUND) != USBHOST_ALLFOUND) |
| { |
| uerr("ERROR: Found IF:%s EPIN:%s\n", |
| (found & USBHOST_IFFOUND) != 0 ? "YES" : "NO", |
| (found & USBHOST_EPINFOUND) != 0 ? "YES" : "NO"); |
| return -EINVAL; |
| } |
| |
| /* We are good... Allocate the interrupt IN endpoint. */ |
| |
| ret = DRVR_EPALLOC(hport->drvr, &epindesc, &priv->epin); |
| if (ret < 0) |
| { |
| uerr("ERROR: Failed to allocate interrupt IN endpoint\n"); |
| return ret; |
| } |
| |
| uinfo("Endpoint allocated\n"); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_devinit |
| * |
| * Description: |
| * The USB device has been successfully connected. This completes the |
| * initialization operations. It is first called after the |
| * configuration descriptor has been received. |
| * |
| * This function is called from the connect() method. This function always |
| * executes on the thread of the caller of connect(). |
| * |
| * Input Parameters: |
| * priv - A reference to the class instance. |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static inline int usbhost_devinit(FAR struct usbhost_state_s *priv) |
| { |
| char devname[DEV_NAMELEN]; |
| int ret; |
| |
| /* Set aside a transfer buffer for exclusive |
| * use by the mouse class driver |
| */ |
| |
| ret = usbhost_tdalloc(priv); |
| if (ret < 0) |
| { |
| uerr("ERROR: Failed to allocate transfer buffer\n"); |
| return ret; |
| } |
| |
| /* Increment the reference count. This will prevent usbhost_destroy() from |
| * being called asynchronously if the device is removed. |
| */ |
| |
| priv->crefs++; |
| DEBUGASSERT(priv->crefs == 2); |
| |
| /* Start a worker task to poll the USB device. It would be nice to use |
| * the NuttX worker thread to do this, but this task needs to wait for |
| * events and activities on the worker thread should not involve |
| * significant waiting. Having a dedicated thread is more efficient in |
| * this sense, but requires more memory resources, primarily for the |
| * dedicated stack (CONFIG_HIDMOUSE_STACKSIZE). |
| */ |
| |
| uinfo("Start poll task\n"); |
| |
| /* The inputs to a task started by kthread_create() are very awkward for |
| * this purpose. They are really designed for command line tasks |
| * (argc/argv). So the following is kludge pass binary data when the |
| * mouse poll task is started. |
| * |
| * First, make sure we have exclusive access to g_priv (what is the |
| * likelihood of this being used? About zero, but we protect it anyway). |
| */ |
| |
| ret = nxmutex_lock(&g_lock); |
| if (ret < 0) |
| { |
| usbhost_tdfree(priv); |
| goto errout; |
| } |
| |
| g_priv = priv; |
| |
| ret = kthread_create("mouse", CONFIG_HIDMOUSE_DEFPRIO, |
| CONFIG_HIDMOUSE_STACKSIZE, usbhost_mouse_poll, NULL); |
| if (ret < 0) |
| { |
| /* Failed to started the poll thread... |
| * probably due to memory resources |
| */ |
| |
| nxmutex_unlock(&g_lock); |
| goto errout; |
| } |
| |
| priv->pollpid = (pid_t)ret; |
| |
| /* Now wait for the poll task to get properly initialized */ |
| |
| ret = nxsem_wait_uninterruptible(&g_syncsem); |
| nxmutex_unlock(&g_lock); |
| |
| if (ret < 0) |
| { |
| goto errout; |
| } |
| |
| /* Register the driver */ |
| |
| uinfo("Register driver\n"); |
| usbhost_mkdevname(priv, devname); |
| ret = register_driver(devname, &g_hidmouse_fops, 0666, priv); |
| |
| /* We now have to be concerned about asynchronous modification of crefs |
| * because the driver has been registered. |
| */ |
| |
| errout: |
| nxmutex_lock(&priv->lock); |
| priv->crefs--; |
| nxmutex_unlock(&priv->lock); |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_getle16 |
| * |
| * Description: |
| * Get a (possibly unaligned) 16-bit little endian value. |
| * |
| * Input Parameters: |
| * val - A pointer to the first byte of the little endian value. |
| * |
| * Returned Value: |
| * A uint16_t representing the whole 16-bit integer value |
| * |
| ****************************************************************************/ |
| |
| static inline uint16_t usbhost_getle16(const uint8_t *val) |
| { |
| return (uint16_t)val[1] << 8 | (uint16_t)val[0]; |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_tdalloc |
| * |
| * Description: |
| * Allocate transfer buffer memory. |
| * |
| * Input Parameters: |
| * priv - A reference to the class instance. |
| * |
| * Returned Value: |
| * On success, zero (OK) is returned. On failure, an negated errno value |
| * is returned to indicate the nature of the failure. |
| * |
| ****************************************************************************/ |
| |
| static inline int usbhost_tdalloc(FAR struct usbhost_state_s *priv) |
| { |
| FAR struct usbhost_hubport_s *hport; |
| |
| DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL && |
| priv->tbuffer == NULL); |
| hport = priv->usbclass.hport; |
| |
| return DRVR_ALLOC(hport->drvr, &priv->tbuffer, &priv->tbuflen); |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_tdfree |
| * |
| * Description: |
| * Free transfer buffer memory. |
| * |
| * Input Parameters: |
| * priv - A reference to the class instance. |
| * |
| * Returned Value: |
| * On success, zero (OK) is returned. On failure, an negated errno value |
| * is returned to indicate the nature of the failure. |
| * |
| ****************************************************************************/ |
| |
| static inline int usbhost_tdfree(FAR struct usbhost_state_s *priv) |
| { |
| FAR struct usbhost_hubport_s *hport; |
| int result = OK; |
| DEBUGASSERT(priv); |
| |
| if (priv->tbuffer) |
| { |
| DEBUGASSERT(priv->usbclass.hport); |
| hport = priv->usbclass.hport; |
| result = DRVR_FREE(hport->drvr, priv->tbuffer); |
| priv->tbuffer = NULL; |
| priv->tbuflen = 0; |
| } |
| |
| return result; |
| } |
| |
| /**************************************************************************** |
| * struct usbhost_registry_s methods |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: usbhost_create |
| * |
| * Description: |
| * This function implements the create() method of struct |
| * usbhost_registry_s. The create() method is a callback into the class |
| * implementation. It is used to (1) create a new instance of the USB |
| * host class state and to (2) bind a USB host driver "session" to the |
| * class instance. Use of this create() method will support environments |
| * where there may be multiple USB ports and multiple USB devices |
| * simultaneously connected. |
| * |
| * Input Parameters: |
| * hport - The hub port that manages the new class instance. |
| * id - In the case where the device supports multiple base classes, |
| * subclasses, or protocols, this specifies which to configure for. |
| * |
| * Returned Value: |
| * On success, this function will return a non-NULL instance of struct |
| * usbhost_class_s that can be used by the USB host driver to communicate |
| * with the USB host class. NULL is returned on failure; this function |
| * will fail only if the hport input parameter is NULL or if there are |
| * insufficient resources to create another USB host class instance. |
| * |
| ****************************************************************************/ |
| |
| static FAR struct usbhost_class_s * |
| usbhost_create(FAR struct usbhost_hubport_s *hport, |
| FAR const struct usbhost_id_s *id) |
| { |
| FAR struct usbhost_state_s *priv; |
| |
| /* Allocate a USB host class instance */ |
| |
| priv = usbhost_allocclass(); |
| if (priv) |
| { |
| /* Initialize the allocated storage class instance */ |
| |
| memset(priv, 0, sizeof(struct usbhost_state_s)); |
| |
| /* Assign a device number to this class instance */ |
| |
| if (usbhost_allocdevno(priv) == OK) |
| { |
| /* Initialize class method function pointers */ |
| |
| priv->usbclass.hport = hport; |
| priv->usbclass.connect = usbhost_connect; |
| priv->usbclass.disconnected = usbhost_disconnected; |
| |
| /* The initial reference count is 1... One reference is held by |
| * the driver's usbhost_mouse_poll() task. |
| */ |
| |
| priv->crefs = 1; |
| |
| /* Initialize mutex & semaphores */ |
| |
| nxmutex_init(&priv->lock); |
| nxsem_init(&priv->waitsem, 0, 0); |
| |
| /* Return the instance of the USB mouse class driver */ |
| |
| return &priv->usbclass; |
| } |
| } |
| |
| /* An error occurred. Free the allocation and return NULL on all failures */ |
| |
| if (priv) |
| { |
| usbhost_freeclass(priv); |
| } |
| |
| return NULL; |
| } |
| |
| /**************************************************************************** |
| * struct usbhost_class_s methods |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: usbhost_connect |
| * |
| * Description: |
| * This function implements the connect() method of struct |
| * usbhost_class_s. This method is a callback into the class |
| * implementation. It is used to provide the device's configuration |
| * descriptor to the class so that the class may initialize properly |
| * |
| * Input Parameters: |
| * usbclass - The USB host class entry previously obtained from a call to |
| * create(). |
| * configdesc - A pointer to a uint8_t buffer container the configuration |
| * descriptor. |
| * desclen - The length in bytes of the configuration descriptor. |
| * |
| * Returned Value: |
| * On success, zero (OK) is returned. On a failure, a negated errno value |
| * is returned indicating the nature of the failure |
| * |
| * NOTE that the class instance remains valid upon return with a failure. |
| * It is the responsibility of the higher level enumeration logic to call |
| * CLASS_DISCONNECTED to free up the class driver resources. |
| * |
| * Assumptions: |
| * - This function will *not* be called from an interrupt handler. |
| * - If this function returns an error, the USB host controller driver |
| * must call to DISCONNECTED method to recover from the error |
| * |
| ****************************************************************************/ |
| |
| static int usbhost_connect(FAR struct usbhost_class_s *usbclass, |
| FAR const uint8_t *configdesc, int desclen) |
| { |
| FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)usbclass; |
| int ret; |
| |
| DEBUGASSERT(priv != NULL && configdesc != NULL && |
| desclen >= sizeof(struct usb_cfgdesc_s)); |
| |
| /* Parse the configuration descriptor to get the endpoints */ |
| |
| ret = usbhost_cfgdesc(priv, configdesc, desclen); |
| if (ret < 0) |
| { |
| uerr("ERROR: usbhost_cfgdesc() failed: %d\n", ret); |
| } |
| else |
| { |
| /* Now configure the device and register the NuttX driver */ |
| |
| ret = usbhost_devinit(priv); |
| if (ret < 0) |
| { |
| uerr("ERROR: usbhost_devinit() failed: %d\n", ret); |
| } |
| } |
| |
| /* ERROR handling: Do nothing. If we return and error during connection, |
| * the driver is required to call the DISCONNECT method. Possibilities: |
| * |
| * - Failure occurred before the mouse poll task was started successfully. |
| * In this case, the disconnection will have to be handled on the worker |
| * task. |
| * - Failure occurred after the mouse poll task was started successfully. |
| * In this case, the disconnection can be performed on the mouse poll |
| * thread. |
| */ |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_disconnected |
| * |
| * Description: |
| * This function implements the disconnected() method of struct |
| * usbhost_class_s. This method is a callback into the class |
| * implementation. It is used to inform the class that the USB device has |
| * been disconnected. |
| * |
| * Input Parameters: |
| * usbclass - The USB host class entry previously obtained from a call to |
| * create(). |
| * |
| * Returned Value: |
| * On success, zero (OK) is returned. On a failure, a negated errno value |
| * is returned indicating the nature of the failure |
| * |
| * Assumptions: |
| * This function may be called from an interrupt handler. |
| * |
| ****************************************************************************/ |
| |
| static int usbhost_disconnected(FAR struct usbhost_class_s *usbclass) |
| { |
| FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)usbclass; |
| int i; |
| |
| DEBUGASSERT(priv != NULL); |
| |
| /* Set an indication to any users of the mouse device that the device |
| * is no longer available. |
| */ |
| |
| priv->disconnected = true; |
| uinfo("Disconnected\n"); |
| |
| /* Are there a thread(s) waiting for mouse data that will never come? */ |
| |
| for (i = 0; i < priv->nwaiters; i++) |
| { |
| /* Yes.. wake them up */ |
| |
| nxsem_post(&priv->waitsem); |
| } |
| |
| /* Possibilities: |
| * |
| * - Failure occurred before the mouse poll task was started successfully. |
| * In this case, the disconnection will have to be handled on the worker |
| * task. |
| * - Failure occurred after the mouse poll task was started successfully. |
| * In this case, the disconnection can be performed on the mouse poll |
| * thread. |
| */ |
| |
| if (priv->polling) |
| { |
| /* The polling task is still alive. Signal the mouse polling task. |
| * When that task wakes up, it will decrement the reference count and, |
| * perhaps, destroy the class instance. Then it will exit. |
| */ |
| |
| nxsig_kill(priv->pollpid, SIGALRM); |
| } |
| else |
| { |
| /* In the case where the failure occurs before the polling task was |
| * started. Now what? We are probably executing from an interrupt |
| * handler here. We will use the worker thread. This is kind of |
| * wasteful and begs for a re-design. |
| */ |
| |
| DEBUGASSERT(priv->work.worker == NULL); |
| work_queue(HPWORK, &priv->work, usbhost_destroy, priv, 0); |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Character driver methods |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: usbhost_open |
| * |
| * Description: |
| * Standard character driver open method. |
| * |
| ****************************************************************************/ |
| |
| static int usbhost_open(FAR struct file *filep) |
| { |
| FAR struct inode *inode; |
| FAR struct usbhost_state_s *priv; |
| irqstate_t flags; |
| int ret; |
| |
| uinfo("Entry\n"); |
| inode = filep->f_inode; |
| priv = inode->i_private; |
| |
| /* Make sure that we have exclusive access to the private data structure */ |
| |
| DEBUGASSERT(priv && priv->crefs > 0 && priv->crefs < USBHOST_MAX_CREFS); |
| ret = nxmutex_lock(&priv->lock); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| /* Check if the mouse device is still connected. We need to disable |
| * interrupts momentarily to assure that there are no asynchronous |
| * disconnect events. |
| */ |
| |
| flags = enter_critical_section(); |
| if (priv->disconnected) |
| { |
| /* No... the driver is no longer bound to the class. That means that |
| * the USB storage device is no longer connected. Refuse any further |
| * attempts to open the driver. |
| */ |
| |
| ret = -ENODEV; |
| } |
| else |
| { |
| /* Was the driver previously open? We need to perform special |
| * initialization on the first time that the driver is opened. |
| */ |
| |
| if (!priv->open) |
| { |
| /* Set the thresholding values so that the first button press |
| * will be reported. |
| */ |
| |
| priv->xlast = INVALID_POSITION_B16; |
| priv->ylast = INVALID_POSITION_B16; |
| #ifdef CONFIG_INPUT_MOUSE_WHEEL |
| priv->wlast = INVALID_POSITION_B16; |
| #endif |
| /* Set the reported position to the center of the range */ |
| |
| priv->xaccum = (HIDMOUSE_XMAX_B16 >> 1); |
| priv->yaccum = (HIDMOUSE_YMAX_B16 >> 1); |
| } |
| |
| /* Otherwise, just increment the reference count on the driver */ |
| |
| priv->crefs++; |
| priv->open = true; |
| ret = OK; |
| } |
| |
| leave_critical_section(flags); |
| |
| nxmutex_unlock(&priv->lock); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_close |
| * |
| * Description: |
| * Standard character driver close method. |
| * |
| ****************************************************************************/ |
| |
| static int usbhost_close(FAR struct file *filep) |
| { |
| FAR struct inode *inode; |
| FAR struct usbhost_state_s *priv; |
| irqstate_t flags; |
| int ret; |
| |
| uinfo("Entry\n"); |
| inode = filep->f_inode; |
| priv = inode->i_private; |
| |
| /* Decrement the reference count on the driver */ |
| |
| DEBUGASSERT(priv->crefs >= 1); |
| ret = nxmutex_lock(&priv->lock); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| /* We need to disable interrupts momentarily to assure that there are no |
| * asynchronous poll or disconnect events. |
| */ |
| |
| flags = enter_critical_section(); |
| priv->crefs--; |
| |
| /* Check if the USB mouse device is still connected. If the device is |
| * no longer connected, then unregister the driver and free the driver |
| * class instance. |
| */ |
| |
| if (priv->disconnected) |
| { |
| /* If the reference count is one or less then there are two |
| * possibilities: |
| * |
| * 1) It might be zero meaning that the polling thread has already |
| * exited and decremented its count. |
| * 2) If might be one meaning either that (a) the polling thread is |
| * still running and still holds a count, or (b) the polling thread |
| * has exited, but there is still an outstanding open reference. |
| */ |
| |
| if (priv->crefs == 0 || (priv->crefs == 1 && priv->polling)) |
| { |
| /* Yes.. In either case, then the driver is no longer open */ |
| |
| priv->open = false; |
| |
| /* Check if the USB keyboard device is still connected. */ |
| |
| if (priv->crefs == 0) |
| { |
| /* The polling thread is no longer running */ |
| |
| DEBUGASSERT(!priv->polling); |
| |
| /* If the device is no longer connected, unregister the driver |
| * and free the driver class instance. |
| */ |
| |
| usbhost_destroy(priv); |
| |
| /* Skip giving the semaphore... it is no longer valid */ |
| |
| leave_critical_section(flags); |
| return OK; |
| } |
| else /* if (priv->crefs == 1) */ |
| { |
| /* The polling thread is still running. Signal it so that it |
| * will wake up and call usbhost_destroy(). The particular |
| * signal that we use does not matter in this case. |
| */ |
| |
| nxsig_kill(priv->pollpid, SIGALRM); |
| } |
| } |
| } |
| |
| nxmutex_unlock(&priv->lock); |
| leave_critical_section(flags); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_read |
| * |
| * Description: |
| * Standard character driver read method. |
| * |
| ****************************************************************************/ |
| |
| static ssize_t usbhost_read(FAR struct file *filep, FAR char *buffer, |
| size_t len) |
| { |
| FAR struct inode *inode; |
| FAR struct usbhost_state_s *priv; |
| #ifdef CONFIG_HIDMOUSE_TSCIF |
| FAR struct touch_sample_s *report; |
| #else |
| FAR struct mouse_report_s *report; |
| #endif |
| struct mouse_sample_s sample; |
| int ret; |
| |
| uinfo("Entry\n"); |
| DEBUGASSERT(buffer); |
| inode = filep->f_inode; |
| priv = inode->i_private; |
| |
| /* Make sure that we have exclusive access to the private data structure */ |
| |
| DEBUGASSERT(priv && priv->crefs > 0 && priv->crefs < USBHOST_MAX_CREFS); |
| ret = nxmutex_lock(&priv->lock); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| /* Check if the mouse is still connected. We need to disable interrupts |
| * momentarily to assure that there are no asynchronous disconnect events. |
| */ |
| |
| if (priv->disconnected) |
| { |
| /* No... the driver is no longer bound to the class. That means that |
| * the USB mouse is no longer connected. Refuse any further attempts |
| * to access the driver. |
| */ |
| |
| ret = -ENODEV; |
| goto errout; |
| } |
| |
| /* Try to read sample data. */ |
| |
| ret = usbhost_sample(priv, &sample); |
| if (ret < 0) |
| { |
| /* Sample data is not available now. We would ave to wait to get |
| * receive sample data. If the user has specified the O_NONBLOCK |
| * option, then just return an error. |
| */ |
| |
| if (filep->f_oflags & O_NONBLOCK) |
| { |
| /* Yes.. then return a failure */ |
| |
| ret = -EAGAIN; |
| goto errout; |
| } |
| |
| /* Wait for sample data */ |
| |
| ret = usbhost_waitsample(priv, &sample); |
| if (ret < 0) |
| { |
| /* We might have been awakened by a signal */ |
| |
| ierr("ERROR: usbhost_waitsample: %d\n", ret); |
| goto errout; |
| } |
| } |
| |
| /* We now have sampled HIDMOUSE data that we can report to the caller. */ |
| |
| #ifdef CONFIG_HIDMOUSE_TSCIF |
| report = (FAR struct touch_sample_s *)buffer; |
| memset(report, 0, SIZEOF_TOUCH_SAMPLE_S(1)); |
| |
| report->npoints = 1; |
| report->point[0].id = sample.id; |
| report->point[0].x = sample.x; |
| report->point[0].y = sample.y; |
| |
| /* Report the appropriate flags */ |
| |
| if (sample.event == BUTTON_RELEASED) |
| { |
| /* But was released. Is the positional data valid? This is |
| * important to know because the release will be sent to the |
| * window based on its last positional data. |
| */ |
| |
| if (sample.valid) |
| { |
| report->point[0].flags = TOUCH_UP | TOUCH_ID_VALID | |
| TOUCH_POS_VALID; |
| } |
| else |
| { |
| report->point[0].flags = TOUCH_UP | TOUCH_ID_VALID; |
| } |
| } |
| else if (sample.event == BUTTON_PRESSED) |
| { |
| /* First event */ |
| |
| report->point[0].flags = TOUCH_DOWN | TOUCH_ID_VALID | |
| TOUCH_POS_VALID; |
| } |
| else /* if (sample->event == BUTTON_MOVE) */ |
| { |
| /* Movement of the same event */ |
| |
| report->point[0].flags = TOUCH_MOVE | TOUCH_ID_VALID | |
| TOUCH_POS_VALID; |
| } |
| |
| iinfo(" id: %d\n", report->point[0].id); |
| iinfo(" flags: %02x\n", report->point[0].flags); |
| iinfo(" x: %d\n", report->point[0].x); |
| iinfo(" y: %d\n", report->point[0].y); |
| |
| ret = SIZEOF_TOUCH_SAMPLE_S(1); |
| #else |
| report = (FAR struct mouse_report_s *)buffer; |
| memset(report, 0, sizeof(struct mouse_report_s)); |
| |
| report->buttons = sample.buttons; |
| report->x = sample.x; |
| report->y = sample.y; |
| #ifdef CONFIG_INPUT_MOUSE_WHEEL |
| report->wheel = sample.wheel; |
| #endif |
| |
| ret = sizeof(struct mouse_report_s); |
| #endif |
| |
| errout: |
| nxmutex_unlock(&priv->lock); |
| iinfo("Returning: %d\n", ret); |
| return (ssize_t)ret; |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_write |
| * |
| * Description: |
| * Standard character driver write method. |
| * |
| ****************************************************************************/ |
| |
| static ssize_t usbhost_write(FAR struct file *filep, FAR const char *buffer, |
| size_t len) |
| { |
| /* We won't try to write to the mouse */ |
| |
| return -ENOSYS; |
| } |
| |
| /**************************************************************************** |
| * Name: usbhost_poll |
| * |
| * Description: |
| * Standard character driver poll method. |
| * |
| ****************************************************************************/ |
| |
| static int usbhost_poll(FAR struct file *filep, FAR struct pollfd *fds, |
| bool setup) |
| { |
| FAR struct inode *inode; |
| FAR struct usbhost_state_s *priv; |
| int ret; |
| int i; |
| |
| uinfo("Entry\n"); |
| DEBUGASSERT(fds); |
| inode = filep->f_inode; |
| priv = inode->i_private; |
| |
| /* Make sure that we have exclusive access to the private data structure */ |
| |
| DEBUGASSERT(priv); |
| ret = nxmutex_lock(&priv->lock); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| /* Check if the mouse is still connected. We need to disable interrupts |
| * momentarily to assure that there are no asynchronous disconnect events. |
| */ |
| |
| if (priv->disconnected) |
| { |
| /* No... the driver is no longer bound to the class. That means that |
| * the USB mouse is no longer connected. Refuse any further attempts |
| * to access the driver. |
| */ |
| |
| ret = -ENODEV; |
| } |
| else if (setup) |
| { |
| /* This is a request to set up the poll. Find an available slot for |
| * the poll structure reference |
| */ |
| |
| for (i = 0; i < CONFIG_HIDMOUSE_NPOLLWAITERS; i++) |
| { |
| /* Find an available slot */ |
| |
| if (!priv->fds[i]) |
| { |
| /* Bind the poll structure and this slot */ |
| |
| priv->fds[i] = fds; |
| fds->priv = &priv->fds[i]; |
| break; |
| } |
| } |
| |
| if (i >= CONFIG_HIDMOUSE_NPOLLWAITERS) |
| { |
| fds->priv = NULL; |
| ret = -EBUSY; |
| goto errout; |
| } |
| |
| /* Should we immediately notify on any of the requested events? Notify |
| * the POLLIN event if there is buffered mouse data. |
| */ |
| |
| if (priv->valid) |
| { |
| poll_notify(&fds, 1, POLLIN); |
| } |
| } |
| else |
| { |
| /* This is a request to tear down the poll. */ |
| |
| struct pollfd **slot = (struct pollfd **)fds->priv; |
| DEBUGASSERT(slot); |
| |
| /* Remove all memory of the poll setup */ |
| |
| *slot = NULL; |
| fds->priv = NULL; |
| } |
| |
| errout: |
| nxmutex_unlock(&priv->lock); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: usbhost_mouse_init |
| * |
| * Description: |
| * Initialize the USB storage HID mouse class driver. This function |
| * should be called be platform-specific code in order to initialize and |
| * register support for the USB host HID mouse class device. |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * On success this function will return zero (OK); A negated errno value |
| * will be returned on failure. |
| * |
| ****************************************************************************/ |
| |
| int usbhost_mouse_init(void) |
| { |
| /* Advertise our availability to support (certain) mouse devices */ |
| |
| return usbhost_registerclass(&g_hidmouse); |
| } |
| |
| #endif /* CONFIG_USBHOST)&& !CONFIG_USBHOST_INT_DISABLE */ |