| /**************************************************************************** |
| * arch/renesas/src/rx65n/rx65n_usbhost.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 <stdint.h> |
| #include <stdbool.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <semaphore.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <debug.h> |
| |
| #include <nuttx/arch.h> |
| #include <nuttx/signal.h> |
| #include <nuttx/mutex.h> |
| #include <nuttx/semaphore.h> |
| #include <nuttx/usb/usb.h> |
| #include <nuttx/usb/ohci.h> |
| #include <nuttx/usb/usbhost.h> |
| #include <nuttx/usb/usbhost_devaddr.h> |
| #include <nuttx/usb/hid.h> |
| |
| #include <nuttx/kmalloc.h> |
| |
| #include <nuttx/irq.h> |
| |
| #include <arch/board/board.h> |
| |
| #include "renesas_internal.h" |
| #include "chip.h" |
| #include "rx65n_usbhost.h" |
| |
| #include "arch/rx65n/iodefine.h" |
| #include "rx65n_cmtw.h" |
| |
| #define INTSTS0_BIT_VALUES_TO_ACK (0xd870u) |
| #define INTSTS1_BIT_VALUES_TO_ACK (0xd870u) |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* Configuration ************************************************************/ |
| |
| /* All I/O buffers must lie in AHB SRAM because of the OHCI DMA. |
| * It might be okay if no I/O buffers are used *IF* the application |
| * can guarantee that all end-user I/O buffers reside in AHB SRAM. |
| */ |
| |
| #if defined(CONFIG_USBHOST) && defined(CONFIG_USBDEV) |
| # error "Both USB Host & Device cannot be configured" |
| #endif |
| |
| #ifndef CONFIG_RX65N_USBHOST_NPREALLOC |
| # define CONFIG_RX65N_USBHOST_NPREALLOC 8 |
| #endif |
| |
| #ifndef CONFIG_DEBUG_USB_INFO |
| # undef CONFIG_RX65N_USBHOST_REGDEBUG |
| #endif |
| |
| /* USB Host Memory **********************************************************/ |
| |
| /* Periodic intervals 2, 4, 8, 16,and 32 supported */ |
| |
| #define MIN_PERINTERVAL 2 |
| #define MAX_PERINTERVAL 32 |
| |
| /* Descriptors **************************************************************/ |
| |
| /* TD delay interrupt value */ |
| |
| #define TD_DELAY(n) (uint32_t)((n) << GTD_STATUS_DI_SHIFT) |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| /* This structure retains the state of the USB host controller */ |
| |
| struct rx65n_usbhost_s |
| { |
| /* Common device fields. This must be the first thing defined in the |
| * structure so that it is possible to simply cast from struct usbhost_s |
| * to struct rx65n_usbhost_s. |
| */ |
| |
| struct usbhost_driver_s drvr; |
| |
| /* This is the hub port description understood by class drivers */ |
| |
| struct usbhost_roothubport_s rhport; |
| |
| /* Driver status */ |
| |
| volatile bool change; /* Connection change */ |
| volatile bool connected; /* Connected to device */ |
| |
| /* Thread is waiting for a port status change */ |
| |
| volatile bool pscwait; /* Thread is waiting for a port status change */ |
| |
| #ifndef CONFIG_USBHOST_INT_DISABLE |
| |
| /* Minimum periodic IN EP polling interval: 2, 4, 6, 16, or 32 */ |
| |
| uint8_t ininterval; |
| |
| /* Minimum periodic IN EP polling interval: 2, 4, 6, 16, or 32 */ |
| |
| uint8_t outinterval; |
| |
| #endif |
| |
| mutex_t lock; /* Support mutually exclusive access */ |
| |
| /* Semaphore to wait Write-back Done Head event */ |
| |
| sem_t pscsem; |
| struct work_s rx65n_interrupt_bhalf; /* Supports interrupt bottom half */ |
| |
| #ifdef CONFIG_USBHOST_HUB |
| |
| /* Used to pass external hub port events */ |
| |
| volatile struct usbhost_hubport_s *hport; |
| #endif |
| |
| struct usbhost_devaddr_s devgen; /* Address generation data */ |
| }; |
| |
| /* This structure describes one asynchronous transfer */ |
| |
| struct rx65n_usbhost_xfrinfo_s |
| { |
| volatile bool wdhwait; /* Thread is waiting for WDH interrupt */ |
| |
| /* TD control status bits from last Write-back Done Head event */ |
| |
| volatile uint8_t tdstatus; |
| uint8_t *buffer; /* Transfer buffer start */ |
| uint16_t buflen; /* Buffer length */ |
| uint16_t xfrd; /* Number of bytes transferred */ |
| volatile uint8_t tdxfercond; |
| |
| /* New variable. added |
| * TD transfer condition : used for deciding |
| * what to do in the interrupt/bottom |
| * half context |
| */ |
| |
| #ifdef CONFIG_USBHOST_ASYNCH |
| #if RX65N_USBHOST_IOBUFFERS > 0 |
| |
| /* Remember the allocated DMA buffer address so that it can be freed when |
| * the transfer completes. |
| */ |
| |
| uint8_t *alloc; /* Allocated buffer */ |
| #endif |
| |
| /* Retain the callback information for the asynchronous transfer |
| * completion. |
| */ |
| |
| usbhost_asynch_t callback; /* Transfer complete callback */ |
| void *arg; /* Argument that accompanies the callback */ |
| #endif |
| }; |
| |
| /* The OCHI expects the size of an endpoint descriptor to be 16 bytes. |
| * However, the size allocated for an endpoint descriptor is 32 bytes. |
| * This extra 16-bytes is used by the OHCI host driver in |
| * order to maintain additional endpoint-specific data. |
| */ |
| |
| struct rx65n_usbhost_ed_s |
| { |
| /* Hardware specific fields */ |
| |
| struct ohci_ed_s hw; /* 0-15 */ |
| |
| /* Software specific fields */ |
| |
| /* 16: Transfer type. See SB_EP_ATTR_XFER_* in usb.h */ |
| |
| uint8_t xfrtype; |
| |
| /* 17: Periodic EP polling interval: 2, 4, 6, 16, or 32 */ |
| |
| uint8_t interval; |
| |
| /* 18: Semaphore used to wait for Writeback Done Head event */ |
| |
| sem_t wdhsem; |
| |
| /* Pointer to struct that manages asynchronous transfers on this pipe */ |
| |
| /* 16: pipe number associated with with this Endpoint */ |
| |
| uint8_t pipenum; |
| |
| struct rx65n_usbhost_xfrinfo_s *xfrinfo; |
| }; |
| |
| struct rx65n_usbhost_gtd_s |
| { |
| /* Hardware specific fields */ |
| |
| struct ohci_gtd_s hw; |
| |
| /* Software specific fields */ |
| |
| struct rx65n_usbhost_ed_s *ed; /* Pointer to parent ED */ |
| uint8_t pad[12]; |
| }; |
| |
| /* The following is used to manage lists of free EDs, TDs, and TD buffers */ |
| |
| struct rx65n_usbhost_list_s |
| { |
| struct rx65n_usbhost_list_s *flink; |
| |
| /* Link to next buffer in the list */ |
| |
| /* Variable length buffer data follows */ |
| }; |
| |
| struct rx65n_usbhost_ed_s aligned_data(32); |
| struct rx65n_usbhost_gtd_s aligned_data(32); |
| |
| /* This must be aligned to a 256-byte boundary */ |
| |
| static struct ohci_hcca_s g_hcca aligned_data(256); |
| static struct ohci_hcca_s *HCCA; |
| |
| static struct rx65n_usbhost_gtd_s *TDTAIL; |
| |
| /* static struct rx65n_usbhost_ed_s g_rx65n_ep0ed; */ |
| |
| static struct rx65n_usbhost_ed_s *EDCTRL; |
| |
| static struct rx65n_usbhost_gtd_s |
| g_rx65n_tdlist[CONFIG_RX65N_USBHOST_NTDS]; |
| static struct rx65n_usbhost_ed_s |
| g_rx65n_edlist[CONFIG_RX65N_USBHOST_NEDS + 1]; |
| static uint8_t nrdy_retries[CONFIG_RX65N_USBHOST_NEDS + 1]; |
| #define RX65N_TD_BUF_SIZE (CONFIG_RX65N_USBHOST_TDBUFFERS * \ |
| CONFIG_RX65N_USBHOST_TDBUFSIZE) |
| static uint8_t g_tdbuffer [RX65N_TD_BUF_SIZE]; |
| |
| static uint8_t g_kbdport; |
| static uint16_t g_usbidx; |
| static uint8_t g_hubkbd; |
| static uint8_t g_kbdpipe; |
| static uint8_t g_attached; |
| static uint8_t g_detached; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| /* Register operations ******************************************************/ |
| |
| #define rx65n_usbhost_getreg(addr) getreg16((volatile short *)addr) |
| #define rx65n_usbhost_putreg(val,addr) putreg16(val,(volatile short *)addr) |
| static void rx65n_usbhost_setbit(volatile short *regadd, |
| uint16_t setbitval); |
| static void rx65n_usbhost_clearbit(volatile short *regadd, |
| uint16_t clearbitval); |
| |
| /* Semaphores ***************************************************************/ |
| |
| /* Byte stream access helper functions **************************************/ |
| |
| static inline uint16_t rx65n_usbhost_getle16(const uint8_t *val); |
| |
| /* OHCI memory pool helper functions ****************************************/ |
| |
| static inline void rx65n_usbhost_edfree(struct rx65n_usbhost_ed_s *ed); |
| static struct rx65n_usbhost_gtd_s *rx65n_usbhost_tdalloc(uint8_t epnum); |
| static void rx65n_usbhost_tdfree(struct rx65n_usbhost_gtd_s *buffer); |
| static uint8_t *rx65n_usbhost_tballoc(void); |
| static void rx65n_usbhost_tbfree(uint8_t *buffer); |
| #if RX65N_USBHOST_IOBUFFERS > 0 |
| static uint8_t *rx65n_usbhhost_allocio(void); |
| static void rx65n_usbhhost_freeio(uint8_t *buffer); |
| #endif |
| static struct rx65n_usbhost_xfrinfo_s *rx65n_usbhost_alloc_xfrinfo(void); |
| static void rx65n_usbhost_free_xfrinfo |
| (struct rx65n_usbhost_xfrinfo_s *xfrinfo); |
| |
| /* ED list helper functions *************************************************/ |
| |
| static inline int rx65n_usbhost_addctrled(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed); |
| static inline int rx65n_usbhost_remctrled(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed); |
| |
| static inline int rx65n_usbhost_addbulked(struct rx65n_usbhost_s *priv, |
| const struct usbhost_epdesc_s |
| *epdesc, struct rx65n_usbhost_ed_s |
| *ed); |
| |
| static inline int rx65n_usbhost_rembulked(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed); |
| |
| #if !defined(CONFIG_USBHOST_INT_DISABLE) || !defined(CONFIG_USBHOST_ISOC_DISABLE) |
| static unsigned int rx65n_usbhost_getinterval(uint8_t interval); |
| static void rx65n_usbhost_setinttab(uint32_t value, unsigned int interval, |
| unsigned int offset); |
| #endif |
| |
| static inline int rx65n_usbhost_addinted(struct rx65n_usbhost_s *priv, |
| const struct usbhost_epdesc_s *epdesc, |
| struct rx65n_usbhost_ed_s *ed); |
| static inline int rx65n_usbhost_reminted(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed); |
| |
| static inline int rx65n_usbhost_addisoced(struct rx65n_usbhost_s *priv, |
| const struct usbhost_epdesc_s *epdesc, |
| struct rx65n_usbhost_ed_s *ed); |
| static inline int rx65n_usbhost_remisoced(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed); |
| |
| /* Descriptor helper functions **********************************************/ |
| |
| static int rx65n_usbhost_enqueuetd(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed, uint32_t dirpid, |
| uint32_t toggle, volatile uint8_t *buffer, |
| size_t buflen); |
| static int rx65n_usbhost_ctrltd(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed, |
| uint32_t dirpid, uint8_t *buffer, size_t buflen); |
| |
| /* Interrupt handling *******************************************************/ |
| |
| static int rx65n_usbhost_usbinterrupt(int irq, void *context, void *arg); |
| |
| /* USB host controller operations *******************************************/ |
| |
| static int rx65n_usbhost_wait(struct usbhost_connection_s *conn, |
| struct usbhost_hubport_s **hport); |
| static int rx65n_usbhost_rh_enumerate(struct usbhost_connection_s *conn, |
| struct usbhost_hubport_s *hport); |
| static int rx65n_usbhost_enumerate(struct usbhost_connection_s *conn, |
| struct usbhost_hubport_s *hport); |
| |
| static int rx65n_usbhost_ep0configure(struct usbhost_driver_s *drvr, |
| usbhost_ep_t ep0, uint8_t funcaddr, |
| uint8_t speed, uint16_t maxpacketsize); |
| static int rx65n_usbhost_epalloc(struct usbhost_driver_s *drvr, |
| const struct usbhost_epdesc_s *epdesc, |
| usbhost_ep_t *ep); |
| static int rx65n_usbhost_epfree(struct usbhost_driver_s *drvr, |
| usbhost_ep_t ep); |
| static int rx65n_usbhost_alloc(struct usbhost_driver_s *drvr, |
| uint8_t **buffer, size_t *maxlen); |
| static int rx65n_usbhost_free(struct usbhost_driver_s *drvr, |
| uint8_t *buffer); |
| static int rx65n_usbhost_ioalloc(struct usbhost_driver_s *drvr, |
| uint8_t **buffer, size_t buflen); |
| static int rx65n_usbhost_iofree(struct usbhost_driver_s *drvr, |
| uint8_t *buffer); |
| static int rx65n_usbhost_ctrlin(struct usbhost_driver_s *drvr, |
| usbhost_ep_t ep0, |
| const struct usb_ctrlreq_s *req, |
| uint8_t *buffer); |
| static int rx65n_usbhost_ctrlout(struct usbhost_driver_s *drvr, |
| usbhost_ep_t ep0, |
| const struct usb_ctrlreq_s *req, |
| const uint8_t *buffer); |
| static int rx65n_usbhost_transfer_common(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed, |
| uint8_t *buffer, size_t buflen); |
| #if RX65N_USBHOST_IOBUFFERS > 0 |
| static int rx65n_usbhost_dma_alloc(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed, |
| uint8_t *userbuffer, |
| size_t buflen, uint8_t **alloc); |
| static void rx65n_usbhost_dma_free(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed, |
| uint8_t *userbuffer, |
| size_t buflen, uint8_t *alloc); |
| #endif |
| static ssize_t rx65n_usbhost_transfer(struct usbhost_driver_s *drvr, |
| usbhost_ep_t ep, |
| uint8_t *buffer, size_t buflen); |
| #ifdef CONFIG_USBHOST_ASYNCH |
| static void rx65n_usbhost_asynch_completion(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed); |
| static int rx65n_usbhost_asynch(struct usbhost_driver_s *drvr, |
| usbhost_ep_t ep, uint8_t *buffer, |
| size_t buflen, usbhost_asynch_t callback, |
| void *arg); |
| #endif |
| static int rx65n_usbhost_cancel(struct usbhost_driver_s *drvr, |
| usbhost_ep_t ep); |
| #ifdef CONFIG_USBHOST_HUB |
| static int rx65n_usbhost_connect(struct usbhost_driver_s *drvr, |
| struct usbhost_hubport_s *hport, |
| bool connected); |
| #endif |
| static void rx65n_usbhost_disconnect(struct usbhost_driver_s *drvr, |
| struct usbhost_hubport_s *hport); |
| |
| /* Initialization ***********************************************************/ |
| |
| static inline void rx65n_usbhost_ep0init(struct rx65n_usbhost_s *priv); |
| |
| /* Prototype for USB Private Functions */ |
| |
| static void usb_cstd_set_nak(uint16_t pipe); |
| static void usb_cstd_clr_transaction_counter(uint16_t trnreg); |
| void usb_hstd_read_lnst(uint16_t *buf); |
| void usb_hstd_chk_sof(void); |
| void usb_hstd_attach(uint16_t result); |
| void usb_hstd_detach(void); |
| static void usb_cstd_chg_curpipe(uint16_t pipe, uint16_t fifosel, |
| uint16_t isel); |
| static void usb_cstd_do_aclrm(uint16_t pipe); |
| static void usb_cstd_set_buf(uint16_t pipe); |
| static void usb_cstd_clr_stall(uint16_t pipe); |
| static void usb_cstd_pipe_init(uint16_t pipe); |
| void usb_hstd_bus_reset(void); |
| uint16_t usb_hstd_detach_process(void); |
| static uint16_t usb_cstd_get_pipe_dir(uint16_t pipe); |
| static void usb_cstd_clr_pipe_cnfg(uint16_t pipe_no); |
| |
| static void *hw_usb_get_fifosel_adr(uint16_t pipemode); |
| static void *hw_usb_get_fifoctr_adr(uint16_t pipemode); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* In this driver implementation, support is provided for only a single a |
| * single USB device. All status information can be simply retained in a |
| * single globalinstance. |
| */ |
| |
| static struct rx65n_usbhost_s g_usbhost = |
| { |
| .lock = NXMUTEX_INITIALIZER, |
| .pscsem = SEM_INITIALIZER(0), |
| }; |
| |
| /* This is the connection/enumeration interface */ |
| |
| static struct usbhost_connection_s g_usbconn = |
| { |
| .wait = rx65n_usbhost_wait, |
| .enumerate = rx65n_usbhost_enumerate, |
| }; |
| |
| /* This is a free list of EDs and TD buffers */ |
| |
| static struct rx65n_usbhost_list_s *g_edfree; /* List of unused EDs */ |
| static struct rx65n_usbhost_list_s *g_tdfree; /* List of unused TDs */ |
| static struct rx65n_usbhost_list_s *g_tbfree; /* List of unused TBs */ |
| #if RX65N_USBHOST_IOBUFFERS > 0 |
| static struct rx65n_usbhost_list_s *g_iofree; /* unused IO buffers */ |
| #endif |
| |
| /* Pool and free-list of transfer structures */ |
| |
| static struct rx65n_usbhost_list_s *g_xfrfree; |
| static struct rx65n_usbhost_xfrinfo_s g_xfrbuffers |
| [CONFIG_RX65N_USBHOST_NPREALLOC]; |
| |
| /* Prototype for interrupt bottom half function */ |
| |
| static void rx65n_usbhost_bottomhalf(void *arg); |
| |
| typedef struct usb_pipe_table |
| { |
| uint16_t use_flag; |
| uint16_t pipe_cfg; |
| uint16_t pipe_maxp; |
| uint16_t pipe_peri; |
| } usb_pipe_table_t; |
| |
| usb_pipe_table_t g_usb_pipe_table[USB_MAX_PIPE_NUM + 1]; |
| |
| uint8_t kbd_report_data[8]; |
| uint8_t kbd_interrupt_in_pipe = 0; |
| |
| /**************************************************************************** |
| * Public Data |
| ****************************************************************************/ |
| |
| /* Prototypes to avoid errors */ |
| |
| static uint16_t usb_cstd_get_buf_size(uint16_t pipe); |
| static uint16_t usb_cstd_is_set_frdy(uint16_t pipe, uint16_t fifosel, |
| uint16_t isel); |
| static uint16_t usb_cstd_get_maxpacket_size(uint16_t pipe); |
| uint8_t *usb_hstd_read_fifo(uint16_t count, uint16_t pipemode, |
| uint8_t *read_p); |
| static uint16_t usb_cstd_get_pid(uint16_t pipe); |
| uint8_t *usb_hstd_write_fifo(uint16_t count, uint16_t pipemode, |
| uint8_t *write_p); |
| static void usb_cstd_set_transaction_counter(uint16_t trnreg, |
| uint16_t trncnt); |
| static void usb_cstd_nrdy_enable(uint16_t pipe); |
| void usb_hstd_buf_to_fifo(uint8_t *buffer, size_t buflen, uint16_t pipe, |
| uint16_t useport); |
| void usb_hstd_forced_termination(uint16_t pipe, uint16_t status); |
| void usb_hstd_nrdy_endprocess(uint16_t pipe); |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| static void rx65n_usbhost_setbit(volatile short *regadd, |
| uint16_t setbit_val) |
| { |
| volatile uint16_t value_to_set; |
| value_to_set = rx65n_usbhost_getreg(regadd); |
| value_to_set = value_to_set | setbit_val; |
| rx65n_usbhost_putreg(value_to_set, regadd); |
| } |
| |
| static void rx65n_usbhost_clearbit(volatile short *regadd, |
| uint16_t clearbit_val) |
| { |
| volatile uint16_t value_to_clear; |
| value_to_clear = rx65n_usbhost_getreg(regadd); |
| value_to_clear = value_to_clear & (~clearbit_val); |
| rx65n_usbhost_putreg(value_to_clear, regadd); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_chattaring |
| * Description : Remove chattaring processing |
| * Arguments : uint16_t *syssts : SYSSTS register value |
| * Return value : LNST bit value |
| ****************************************************************************/ |
| |
| uint16_t usb_chattaring(uint16_t *syssts) |
| { |
| uint16_t lnst[4]; |
| |
| /* WAIT_LOOP */ |
| |
| while (1) |
| { |
| lnst[0] = (*syssts) & RX65N_USB_SYSSTS0_LNST; |
| up_mdelay(1); /* 1ms wait */ |
| lnst[1] = (*syssts) & RX65N_USB_SYSSTS0_LNST; |
| up_mdelay(1); /* 1ms wait */ |
| lnst[2] = (*syssts) & RX65N_USB_SYSSTS0_LNST; |
| if ((lnst[0] == lnst[1]) && (lnst[0] == lnst[2])) |
| { |
| break; |
| } |
| } |
| |
| return lnst[0]; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hset_enb_ovrcre |
| * Description : Set specified port's OVRCRE-bit (Overcurrent Input |
| * : Change Interrupt Status Enable) in the INTENB1 register. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hset_enb_ovrcre(void) |
| { |
| rx65n_usbhost_setbit(RX65N_USB_INTENB1, RX65N_USB_INTENB1_OVRCRE); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hclear_enb_ovrcre |
| * Description : Clear the OVRCRE-bit of the specified port's INTENB1 |
| * : register, to prohibit VBUS interrupts. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hclear_enb_ovrcre(void) |
| { |
| rx65n_usbhost_clearbit(RX65N_USB_INTENB1, RX65N_USB_INTENB1_OVRCRE); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hset_enb_bchge |
| * Description : The BCHGE-bit (USB Bus Change Interrupt Enable) is set |
| * : in the specified port's INTENB1 register. This will |
| * : cause a BCHG interrupt when a change of USB bus state |
| * : has been detected. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hset_enb_bchge(void) |
| { |
| rx65n_usbhost_setbit(RX65N_USB_INTENB1, RX65N_USB_INTENB1_BCHGE); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hclear_enb_bchge |
| * Description : The BCHGE-bit (USB Bus Change Interrupt Enable) is |
| * : cleared in the specified port's INTENB1 register. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hclear_enb_bchge(void) |
| { |
| rx65n_usbhost_clearbit(RX65N_USB_INTENB1, RX65N_USB_INTENB1_BCHGE); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hset_enb_dtche |
| * Description : Enable the specified port's DTCHE-interrupt |
| * : "Disconnection Detection" by setting the DTCHE-bit. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hset_enb_dtche(void) |
| { |
| rx65n_usbhost_setbit(RX65N_USB_INTENB1, RX65N_USB_INTENB1_DTCHE); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hclear_enb_dtche |
| * Description : Disable the specified port's DTCHE-interrupt |
| * : "Disconnection Detection" by clearing the DTCHE-bit. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hclear_enb_dtche(void) |
| { |
| rx65n_usbhost_clearbit(RX65N_USB_INTENB1, RX65N_USB_INTENB1_DTCHE); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hset_enb_attche |
| * Description : Enable the specified port's ATTCHE-interrupt "Connection |
| * : Detection" by setting the ATTCHE-bit. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hset_enb_attche(void) |
| { |
| rx65n_usbhost_setbit(RX65N_USB_INTENB1, RX65N_USB_INTENB1_ATTCHE); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hclear_enb_attche |
| * Description : Disable the specified port's ATTCHE-interrupt |
| * : "Disconnection Detection" by clearing the ATTCHE-bit. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hclear_enb_attche(void) |
| { |
| rx65n_usbhost_clearbit(RX65N_USB_INTENB1, RX65N_USB_INTENB1_ATTCHE); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hset_enb_signe |
| * Description : Enable the SIGNE-interrupt "Setup Transaction |
| * : Error" by setting the SIGNE-bit. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hset_enb_signe(void) |
| { |
| rx65n_usbhost_setbit(RX65N_USB_INTENB1, RX65N_USB_INTENB1_SIGNE); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hset_enb_sacke |
| * Description : Enable the SACKE-interrupt "Setup Transaction |
| * : Normal Response" by setting the SACKE-bit. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hset_enb_sacke(void) |
| { |
| rx65n_usbhost_setbit(RX65N_USB_INTENB1, RX65N_USB_INTENB1_SACKE); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hclear_sts_ovrcr |
| * Description : Clear the specified port's OVRCR-bit; "Over current |
| * : Input Change Interrupt Status". |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hclear_sts_ovrcr(void) |
| { |
| rx65n_usbhost_putreg(((~RX65N_USB_INTSTS1_OVRCRE) & |
| INTSTS1_BIT_VALUES_TO_ACK), RX65N_USB_INTSTS1); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hclear_sts_bchg |
| * Description : Clear the specified port's BCHG-bit; "USB Bus Change |
| * : Interrupt Status". |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hclear_sts_bchg(void) |
| { |
| rx65n_usbhost_putreg(((~RX65N_USB_INTSTS1_BCHG) & |
| INTSTS1_BIT_VALUES_TO_ACK), RX65N_USB_INTSTS1); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hclear_sts_dtch |
| * Description : Clear the specified port's DTCH-bit; "USB Disconnection |
| * : Detection Interrupt Status". |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hclear_sts_dtch(void) |
| { |
| rx65n_usbhost_putreg(((~RX65N_USB_INTSTS1_DTCH) & |
| INTSTS1_BIT_VALUES_TO_ACK), RX65N_USB_INTSTS1); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hclear_sts_attch |
| * Description : Clear the specified port's ATTCH-bit; "ATTCH Interrupt |
| * : Status". |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hclear_sts_attch(void) |
| { |
| rx65n_usbhost_putreg(((~RX65N_USB_INTSTS1_ATTCH) & |
| INTSTS1_BIT_VALUES_TO_ACK), RX65N_USB_INTSTS1); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hclear_sts_sign |
| * Description : Clear the SIGN-bit; "Setup Transaction Error |
| * : Interrupt Status". |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hclear_sts_sign(void) |
| { |
| rx65n_usbhost_putreg(((~RX65N_USB_INTSTS1_SIGN) & |
| INTSTS1_BIT_VALUES_TO_ACK), RX65N_USB_INTSTS1); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hclear_sts_sack |
| * Description : Clear the SACK-bit; "Setup Transaction Normal |
| * : Response Interrupt Status". |
| * : Interrupt Status". |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hclear_sts_sack(void) |
| { |
| rx65n_usbhost_putreg(((~RX65N_USB_INTSTS1_SACK) & |
| INTSTS1_BIT_VALUES_TO_ACK), RX65N_USB_INTSTS1); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hwrite_dcpctr |
| * Description : Write the specified data value to the DCPCTR register. |
| * Arguments : uint16_t data : Setting value |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hwrite_dcpctr(uint16_t data) |
| { |
| rx65n_usbhost_putreg(data, RX65N_USB_DCPCTR); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hset_sureq |
| * Description : Set te SUREQ-bit in the DCPCTR register |
| * : (Set SETUP packet send when HostController function |
| * : is selected) |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hset_sureq(void) |
| { |
| rx65n_usbhost_setbit(RX65N_USB_DCPCTR, RX65N_USB_DCPCTR_SUREQ); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hread_devadd |
| * Description : Return the DEVADD register value for the specified USB |
| * ; device address. |
| * Arguments : uint16_t devsel : USB device address value |
| * Return value : DEVADDx content |
| ****************************************************************************/ |
| |
| uint16_t hw_usb_hread_devadd(uint16_t devsel) |
| { |
| volatile uint16_t *preg; |
| uint16_t devadr; |
| uint16_t return_value; |
| |
| devadr = devsel >> USB_DEVADDRBIT; |
| |
| if (devadr > USB_MAXDEVADDR) |
| { |
| uerr("ERROR: device address %d is more than max device \ |
| address.\n", devadd); |
| return -ENODEV; |
| } |
| else |
| { |
| preg = (uint16_t *)((RX65N_USB_DEVADD0) + (devadr)); |
| return_value = ((*preg)); |
| return return_value; |
| } |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hset_usbspd |
| * Description : Set the DEVADD register's USBSPD for the specified |
| * : device address. |
| * Arguments : uint16_t devsel : USB device address value |
| * : uint16_t data : The value to write. |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hset_usbspd(uint16_t devsel, uint8_t data) |
| { |
| volatile uint16_t *preg; |
| uint16_t devadr; |
| |
| devadr = devsel; |
| |
| preg = (uint16_t *)(RX65N_USB_DEVADD0 + devadr); |
| |
| (*preg) &= (~RX65N_USB_DEVSPD); |
| |
| (*preg) |= data; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_hmodule_init |
| * Description : USB module initialization for USB Host mode |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_hmodule_init(void) |
| { |
| uint16_t sts; |
| |
| rx65n_usbhost_setbit(RX65N_USB_SYSCFG, RX65N_USB_SYSCFG_SCKE); |
| up_mdelay(1); |
| |
| /* wait until SCKE bit is set */ |
| |
| while (1) |
| { |
| uint16_t regval; |
| regval = rx65n_usbhost_getreg(RX65N_USB_SYSCFG); |
| if (regval & RX65N_USB_SYSCFG_SCKE) |
| break; |
| } |
| |
| putreg32(0x05, RX65N_USB_PHYSLEW); |
| |
| rx65n_usbhost_setbit(RX65N_USB_SYSCFG, RX65N_USB_SYSCFG_DCFM); |
| |
| rx65n_usbhost_setbit(RX65N_USB_SYSCFG, RX65N_USB_SYSCFG_DRPD); |
| |
| sts = usb_chattaring((uint16_t *)RX65N_USB_SYSSTS0); |
| |
| rx65n_usbhost_setbit(RX65N_USB_SYSCFG, RX65N_USB_SYSCFG_USBE); |
| |
| rx65n_usbhost_setbit(RX65N_USB_CFIFOSEL, RX65N_USB_CFIFOSEL_MBW_16); |
| rx65n_usbhost_setbit(RX65N_USB_D0FIFOSEL, RX65N_USB_DFIFOSEL_MBW_16); |
| rx65n_usbhost_setbit(RX65N_USB_D1FIFOSEL, RX65N_USB_DFIFOSEL_MBW_16); |
| |
| switch (sts) |
| { |
| case RX65N_USB_SYSSTS0_LNST_LS_JSTS: |
| case RX65N_USB_SYSSTS0_LNST_FS_JSTS: /* USB device already connected */ |
| |
| syslog(LOG_INFO, "USB Device already connected\n"); |
| rx65n_usbhost_setbit(RX65N_USB_DVSTCTR0, RX65N_USB_DVSTCTR0_USBRST); |
| up_mdelay(20); /* Need to wait greater equal 10ms in USB spec */ |
| rx65n_usbhost_clearbit(RX65N_USB_DVSTCTR0, |
| RX65N_USB_DVSTCTR0_USBRST); |
| |
| /* WAIT_LOOP */ |
| |
| while (((rx65n_usbhost_getreg(RX65N_USB_DVSTCTR0)) & |
| RX65N_USB_DVSTCTR0_RHST) == 4) |
| { |
| /* Wait till the reset is completed */ |
| |
| up_mdelay(1); |
| } |
| |
| if (((rx65n_usbhost_getreg(RX65N_USB_DVSTCTR0)) & |
| RX65N_USB_DVSTCTR0_RHST) == |
| RX65N_USB_DVSTCTR0_SPEED_LOW) |
| { |
| rx65n_usbhost_setbit(RX65N_USB_SOFCFG, |
| RX65N_USB_SOFCFG_TRNENSEL); |
| } |
| |
| rx65n_usbhost_setbit(RX65N_USB_DVSTCTR0, |
| RX65N_USB_DVSTCTR0_UACT); |
| break; |
| |
| case RX65N_USB_SYSSTS0_LNST_SE0: /* USB device not connected */ |
| rx65n_usbhost_setbit(RX65N_USB_INTENB1, RX65N_USB_INTENB1_ATTCHE); |
| break; |
| |
| default: |
| break; |
| } |
| |
| rx65n_usbhost_putreg(((~RX65N_USB_INTSTS1_OVRCRE) & |
| INTSTS1_BIT_VALUES_TO_ACK), RX65N_USB_INTSTS1); |
| rx65n_usbhost_setbit(RX65N_USB_INTENB0, (RX65N_USB_INTENB0_BEMPE | |
| RX65N_USB_INTENB0_NRDYE | RX65N_USB_INTENB0_BRDYE)); |
| rx65n_usbhost_setbit(RX65N_USB_INTENB1, RX65N_USB_INTENB1_ATTCHE); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_read_syscfg |
| * Description : Returns the SYSCFG register value. |
| * Arguments : none |
| * Return value : SYSCFG content. |
| ****************************************************************************/ |
| |
| uint16_t hw_usb_read_syscfg(void) |
| { |
| return rx65n_usbhost_getreg(RX65N_USB_SYSCFG); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_set_usbe |
| * Description : Enable USB operation. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_set_usbe(void) |
| { |
| rx65n_usbhost_setbit(RX65N_USB_SYSCFG, RX65N_USB_SYSCFG_USBE); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_clear_usbe |
| * Description : Enable USB operation. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_clear_usbe(void) |
| { |
| rx65n_usbhost_clearbit(RX65N_USB_SYSCFG, RX65N_USB_SYSCFG_USBE); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_read_syssts |
| * Description : Returns the value of the specified port's SYSSTS |
| * : register. |
| * Arguments : none |
| * Return value : SYSSTS0 content |
| ****************************************************************************/ |
| |
| static uint16_t hw_usb_read_syssts(void) |
| { |
| return rx65n_usbhost_getreg(RX65N_USB_SYSSTS0); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_read_dvstctr |
| * Description : Returns the specified port's DVSTCTR register content. |
| * Arguments : none |
| * Return value : DVSTCTR0 content |
| ****************************************************************************/ |
| |
| static uint16_t hw_usb_read_dvstctr(void) |
| { |
| return rx65n_usbhost_getreg(RX65N_USB_DVSTCTR0); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_rmw_dvstctr |
| * Description : Read-modify-write the specified port's DVSTCTR. |
| * Arguments : uint16_t data : The value to write. |
| * : uint16_t bitptn: Bit pattern to read-modify-write. |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_rmw_dvstctr(uint16_t data, uint16_t bitptn) |
| { |
| uint16_t buf; |
| |
| buf = rx65n_usbhost_getreg(RX65N_USB_DVSTCTR0); |
| buf &= (~bitptn); |
| buf |= (data & bitptn); |
| |
| rx65n_usbhost_putreg(buf, RX65N_USB_DVSTCTR0); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_clear_dvstctr |
| * Description : Clear the bit pattern specified in argument, of the |
| * : specified port's DVSTCTR register. |
| * Arguments : uint16_t bitptn: Bit pattern to read-modify-write. |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_clear_dvstctr(uint16_t bitptn) |
| { |
| rx65n_usbhost_clearbit(RX65N_USB_DVSTCTR0, bitptn); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_set_vbout |
| * Description : Set specified port's VBOUT-bit in the DVSTCTR register. |
| * : (To output a "High" to pin VBOUT.) |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_set_vbout(void) |
| { |
| rx65n_usbhost_setbit(RX65N_USB_DVSTCTR0, RX65N_USB_DVSTCTR0_VBUSEN); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_clear_vbout |
| * Description : Clear specified port's VBOUT-bit in the DVSTCTR register |
| * : (To output a "Low" to pin VBOUT.) |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_clear_vbout(void) |
| { |
| rx65n_usbhost_clearbit(RX65N_USB_DVSTCTR0, RX65N_USB_DVSTCTR0_VBUSEN); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_read_fifo16 |
| * Description : Data is read from the specified pipemode's FIFO register |
| * : 16-bits wide, corresponding to the specified PIPEMODE. |
| * Arguments : uint16_t pipemode : CUSE/D0DMA/D1DMA |
| * Return value : CFIFO/D0FIFO/D1FIFO content (16-bit) |
| ****************************************************************************/ |
| |
| static uint16_t hw_usb_read_fifo16(uint16_t pipemode) |
| { |
| uint16_t data = 0; |
| |
| switch (pipemode) |
| { |
| case RX65N_USB_USING_CFIFO: |
| data = USB0.CFIFO.WORD; |
| break; |
| |
| case RX65N_USB_USING_D0FIFO: |
| data = rx65n_usbhost_getreg(RX65N_USB_D0FIFO); |
| break; |
| |
| case RX65N_USB_USING_D1FIFO: |
| data = rx65n_usbhost_getreg(RX65N_USB_D1FIFO); |
| break; |
| |
| default: |
| syslog(LOG_INFO, "Debug : %s(): Line : %d what is this \ |
| pipe mode?? %d\n", __func__, __LINE__, pipemode); |
| break; |
| } |
| |
| return data; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_write_fifo16 |
| * Description : Data is written to the specified pipemode's FIFO |
| * : register, 16-bits wide, corresponding to the specified |
| * : PIPEMODE. |
| * Arguments : uint16_t pipemode : CUSE/D0DMA/D1DMA |
| * : uint16_t data : Setting value |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_write_fifo16(uint16_t pipemode, uint16_t data) |
| { |
| switch (pipemode) |
| { |
| case RX65N_USB_USING_CFIFO: |
| data = rx65n_usbhost_putreg(data, RX65N_USB_CFIFO); |
| break; |
| |
| case RX65N_USB_USING_D0FIFO: |
| data = rx65n_usbhost_putreg(data, RX65N_USB_D0FIFO); |
| break; |
| |
| case RX65N_USB_USING_D1FIFO: |
| data = rx65n_usbhost_putreg(data, RX65N_USB_D1FIFO); |
| break; |
| |
| default: |
| syslog(LOG_INFO, "Debug : %s(): Line : %d what is this \ |
| pipe mode?? %d\n", __func__, __LINE__, pipemode); |
| break; |
| } |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_write_fifo8 |
| * Description : Data is written to the specified pipemode's FIFO |
| * : register, 8-bits wide, corresponding to the specified |
| * : PIPEMODE. |
| * Arguments : uint16_t pipemode : CUSE/D0DMA/D1DMA |
| * : uint8_t data : Setting value |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_write_fifo8(uint16_t pipemode, uint8_t data) |
| { |
| switch (pipemode) |
| { |
| case USB_CUSE: |
| |
| /* USB0_CFIFO8 = data; */ |
| |
| putreg8(data, RX65N_USB_CFIFO); |
| break; |
| |
| case USB_D0USE: |
| |
| /* USB0_D0FIFO8 = data; */ |
| |
| putreg8(data, RX65N_USB_D0FIFO); |
| break; |
| |
| case USB_D1USE: |
| |
| /* USB0_D1FIFO8 = data; */ |
| |
| putreg8(data, RX65N_USB_D1FIFO); |
| break; |
| |
| default: |
| syslog(LOG_INFO, "Debug : %s(): Line : %d Debug hook...\n", |
| __func__, __LINE__); |
| break; |
| } |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_get_fifosel_adr |
| * Description : Returns the *address* of the FIFOSEL register |
| * : corresponding to specified PIPEMODE. |
| * Arguments : uint16_t pipemode : CUSE/D0DMA/D1DMA |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void *hw_usb_get_fifosel_adr(uint16_t pipemode) |
| { |
| void *p_reg = NULL; |
| |
| switch (pipemode) |
| { |
| case RX65N_USB_USING_CFIFO: |
| p_reg = (void *)RX65N_USB_CFIFOSEL; |
| break; |
| |
| case RX65N_USB_USING_D0FIFO: |
| p_reg = (void *)RX65N_USB_D0FIFOSEL; |
| break; |
| |
| case RX65N_USB_USING_D1FIFO: |
| p_reg = (void *)RX65N_USB_D1FIFOSEL; |
| break; |
| |
| default: |
| syslog(LOG_INFO, |
| "Debug : %s(): Line : %d what is this pipe mode?? %d\n", |
| __func__, __LINE__, pipemode); |
| break; |
| } |
| |
| return p_reg; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_read_fifosel |
| * Description : Returns the value of the specified pipemode's FIFOSEL |
| * : register. |
| * Arguments : uint16_t pipemode : CUSE/D0DMA/D1DMA |
| * Return value : FIFOSEL content |
| ****************************************************************************/ |
| |
| static uint16_t hw_usb_read_fifosel(uint16_t pipemode) |
| { |
| volatile uint16_t *p_reg; |
| |
| p_reg = (uint16_t *)hw_usb_get_fifosel_adr(pipemode); |
| |
| return *p_reg; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_rmw_fifosel |
| * Description : Data is written to the specified pipemode's FIFOSEL |
| * : register (the FIFOSEL corresponding to the specified |
| * : PIPEMODE), using read-modify-write. |
| * Arguments : uint16_t pipemode : CUSE/D0DMA/D1DMA |
| * : uint16_t data : Setting value |
| * : uint16_t bitptn : bitptn: Bit pattern to |
| * : read-modify-write. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_rmw_fifosel(uint16_t pipemode, uint16_t data, |
| uint16_t bitptn) |
| { |
| uint16_t buf; |
| volatile uint16_t *p_reg; |
| |
| p_reg = (uint16_t *)hw_usb_get_fifosel_adr(pipemode); |
| |
| buf = *p_reg; |
| buf &= (~bitptn); |
| buf |= (data & bitptn); |
| *p_reg = buf; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_set_mbw |
| * Description : Set MBW-bits (CFIFO Port Access Bit Width) of the |
| * : FIFOSEL corresponding to the specified PIPEMODE, to |
| * : select 8 or 16-bit wide FIFO port access. |
| * Arguments : uint16_t pipemode : CUSE/D0DMA/D1DMA |
| * : uint16_t data : Setting value |
| * : (data = 0x0400), 32 bit (data = 0x0800) access mode. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_set_mbw(uint16_t pipemode, uint16_t data) |
| { |
| volatile uint16_t *p_reg; |
| |
| p_reg = hw_usb_get_fifosel_adr(pipemode); |
| (*p_reg) &= (~RX65N_USB_CFIFOSEL_MBW_16); |
| |
| if (0 != data) |
| { |
| (*p_reg) |= RX65N_USB_CFIFOSEL_MBW_16; |
| } |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_set_curpipe |
| * Description : Set pipe to the number given; in the FIFOSEL |
| * : corresponding to specified PIPEMODE. |
| * Arguments : uint16_t pipemode : CUSE/D0DMA/D1DMA |
| * : uint16_t pipeno : Pipe number. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_set_curpipe(uint16_t pipemode, uint16_t pipeno) |
| { |
| volatile uint16_t *p_reg; |
| volatile uint16_t reg; |
| |
| p_reg = hw_usb_get_fifosel_adr(pipemode); |
| reg = *p_reg; |
| |
| #ifdef CONFIG_RX65N_USBHOST_DMA |
| if ((USB_D0USE == pipemode) || (USB_D1USE == pipemode)) |
| { |
| if (false == usb_check_use_usba_module(ptr)) |
| { |
| reg &= (~USB_DREQE); |
| } |
| } |
| #endif /* NOT_USING_D0_D1_FIFO */ |
| |
| reg &= (~RX65N_USB_CFIFOSEL_CURPIPE_MASK); |
| *p_reg = reg; |
| |
| reg |= pipeno; |
| |
| *p_reg = reg; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_get_fifoctr_adr |
| * Description : Returns the *address* of the FIFOCTR register |
| * : corresponding to specified PIPEMODE. |
| * : (FIFO Port Control Register.) |
| * Arguments : uint16_t pipemode : CUSE/D0DMA/D1DMA |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void *hw_usb_get_fifoctr_adr(uint16_t pipemode) |
| { |
| void *p_reg = NULL; |
| switch (pipemode) |
| { |
| case RX65N_USB_USING_CFIFO: |
| p_reg = (void *)RX65N_USB_CFIFOCTR; |
| break; |
| |
| case RX65N_USB_USING_D0FIFO: |
| p_reg = (void *)RX65N_USB_D0FIFOCTR; |
| break; |
| |
| case RX65N_USB_USING_D1FIFO: |
| p_reg = (void *)RX65N_USB_D0FIFOCTR; |
| break; |
| |
| default: |
| syslog(LOG_INFO, "Debug : %s(): Line : %d what is this \ |
| pipe mode?? %d\n", |
| __func__, __LINE__, pipemode); |
| break; |
| } |
| |
| return p_reg; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_read_fifoctr |
| * Description : Returns the value of the FIFOCTR register corresponding |
| * : to specified PIPEMODE. |
| * Arguments : uint16_t pipemode : CUSE/D0DMA/D1DMA |
| * Return value : FIFOCTR content |
| ****************************************************************************/ |
| |
| static uint16_t hw_usb_read_fifoctr(uint16_t pipemode) |
| { |
| volatile uint16_t *p_reg; |
| |
| p_reg = (uint16_t *)hw_usb_get_fifoctr_adr(pipemode); |
| |
| return *p_reg; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_set_bval |
| * Description : Set BVAL (Buffer Memory Valid Flag) to the number given; |
| * : in the FIFOCTR corresponding to the specified PIPEMODE. |
| * Arguments : uint16_t pipemode : CUSE/D0DMA/D1DMA |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_set_bval(uint16_t pipemode) |
| { |
| volatile uint16_t *p_reg; |
| |
| p_reg = (uint16_t *)hw_usb_get_fifoctr_adr(pipemode); |
| |
| (*p_reg) |= RX65N_USB_FIFOCTR_BVAL; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_set_bclr |
| * Description : Set BCLR (CPU Buffer Clear) to the number given; in the |
| * : FIFOCTR corresponding to the specified PIPEMODE. |
| * Arguments : uint16_t pipemode : CUSE/D0DMA/D1DMA |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_set_bclr(uint16_t pipemode) |
| { |
| volatile uint16_t *p_reg; |
| |
| p_reg = (uint16_t *)hw_usb_get_fifoctr_adr(pipemode); |
| |
| *p_reg = RX65N_USB_FIFOCTR_BCLR; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_set_intenb |
| * Description : Bit(s) to be set in INTENB register, |
| * : enabling the respective USB interrupt(s). |
| * Arguments : uint16_t data : Bit pattern: Respective interrupts |
| * : with '1' will be enabled. |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_set_intenb(uint16_t data) |
| { |
| rx65n_usbhost_setbit(RX65N_USB_INTENB0, data); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_set_brdyenb |
| * Description : A bit is set in the specified pipe's BRDYENB, enabling |
| * : the respective pipe BRDY interrupt(s). |
| * Arguments : uint16_t pipeno : Pipe number. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_set_brdyenb(uint16_t pipeno) |
| { |
| rx65n_usbhost_setbit(RX65N_USB_BRDYENB, (1 << pipeno)); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_clear_brdyenb |
| * Description : Clear the PIPExBRDYE-bit of the specified pipe to |
| * : prohibit BRDY interrupts of that pipe. |
| * Arguments : uint16_t pipeno : Pipe number. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_clear_brdyenb(uint16_t pipeno) |
| { |
| rx65n_usbhost_clearbit(RX65N_USB_BRDYENB, (1 << pipeno)); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_set_nrdyenb |
| * Description : A bit is set in the specified pipe's NRDYENB, enabling |
| * : the respective pipe NRDY interrupt(s). |
| * Arguments : uint16_t pipeno : Pipe number |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_set_nrdyenb(uint16_t pipeno) |
| { |
| rx65n_usbhost_setbit(RX65N_USB_NRDYENB, (1 << pipeno)); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_clear_nrdyenb |
| * Description : Clear the PIPExNRDYE-bit of the specified pipe to |
| * : prohibit NRDY interrupts of that pipe. |
| * Arguments : uint16_t pipeno : Pipe number. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_clear_nrdyenb(uint16_t pipeno) |
| { |
| rx65n_usbhost_clearbit(RX65N_USB_NRDYENB, (1 << pipeno)); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_set_bempenb |
| * Description : A bit is set in the specified pipe's BEMPENB enabling |
| * : the respective pipe's BEMP interrupt(s). |
| * Arguments : uint16_t pipeno : Pipe number. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_set_bempenb(uint16_t pipeno) |
| { |
| rx65n_usbhost_setbit(RX65N_USB_BEMPENB, (1 << pipeno)); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_clear_bempenb |
| * Description : Clear the PIPExBEMPE-bit of the specified pipe to |
| * : prohibit BEMP interrupts of that pipe. |
| * Arguments : uint16_t pipeno : Pipe number. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_clear_bempenb(uint16_t pipeno) |
| { |
| rx65n_usbhost_clearbit(RX65N_USB_BEMPENB, (1 << pipeno)); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_clear_sts_sofr |
| * Description : Clear the SOFR-bit (Frame Number Refresh Interrupt |
| * : Status) of the clear SOF interrupt status. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_clear_sts_sofr(void) |
| { |
| rx65n_usbhost_clearbit(RX65N_USB_INTSTS0, RX65N_USB_INTSTS0_SOFR); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_clear_sts_brdy |
| * Description : Clear the PIPExBRDY status bit of the specified pipe to |
| * : clear its BRDY interrupt status. |
| * Arguments : uint16_t pipeno: Pipe number. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_clear_sts_brdy(uint16_t pipeno) |
| { |
| rx65n_usbhost_putreg((~(1 << pipeno)) & RX65N_USB_PIPE_ALL, |
| RX65N_USB_BRDYSTS); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_clear_status_nrdy |
| * Description : Clear the PIPExNRDY status bit of the specified pipe to |
| * : clear its NRDY interrupt status. |
| * Arguments : uint16_t pipeno: Pipe number. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_clear_status_nrdy(uint16_t pipeno) |
| { |
| rx65n_usbhost_putreg((~(1 << pipeno)) & RX65N_USB_PIPE_ALL, |
| RX65N_USB_NRDYSTS); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_clear_status_bemp |
| * Description : Clear the PIPExBEMP status bit of the specified pipe to |
| * : its BEMP interrupt status. |
| * Arguments : uint16_t pipeno: Pipe number. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_clear_status_bemp(uint16_t pipeno) |
| { |
| rx65n_usbhost_putreg((~(1 << pipeno)) & RX65N_USB_PIPE_ALL, |
| RX65N_USB_BEMPSTS); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_write_dcpcfg |
| * Description : Specified data is written to DCPCFG register. |
| * Arguments : uint16_t data : Setting value |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_write_dcpcfg(uint16_t data) |
| { |
| rx65n_usbhost_putreg(data, RX65N_USB_DCPCFG); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_read_dcpmaxp |
| * Description : Returns DCPMAXP register content. |
| * Arguments : none |
| * Return value : DCPMAXP content |
| ****************************************************************************/ |
| |
| static uint16_t hw_usb_read_dcpmaxp(void) |
| { |
| return rx65n_usbhost_getreg(RX65N_USB_DCPMAXP); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_write_dcpmxps |
| * Description : Specified data is written to DCPMAXP register. |
| * Arguments : uint16_t data : Setting value |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_write_dcpmxps(uint16_t data) |
| { |
| rx65n_usbhost_putreg(data, RX65N_USB_DCPMAXP); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_write_pipesel |
| * Description : Specified data is written to PIPESEL register. |
| * Arguments : uint16_t data : The value to write. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_write_pipesel(uint16_t data) |
| { |
| rx65n_usbhost_putreg(data, RX65N_USB_PIPESEL); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_read_pipecfg |
| * Description : Returns PIPECFG register content. |
| * Arguments : none |
| * Return value : PIPECFG content |
| ****************************************************************************/ |
| |
| static uint16_t hw_usb_read_pipecfg(void) |
| { |
| return rx65n_usbhost_getreg(RX65N_USB_PIPECFG); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_write_pipecfg |
| * Description : Specified data is written to PIPECFG register. |
| * Arguments : uint16_t data : Setting value |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_write_pipecfg(uint16_t data) |
| { |
| rx65n_usbhost_putreg(data, RX65N_USB_PIPECFG); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_read_pipemaxp |
| * Description : Returns PIPEMAXP register content. |
| * Arguments : none |
| * Return value : PIPEMAXP content |
| ****************************************************************************/ |
| |
| static uint16_t hw_usb_read_pipemaxp(void) |
| { |
| return rx65n_usbhost_getreg(RX65N_USB_PIPEMAXP); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_write_pipemaxp |
| * Description : Specified data is written to PIPEMAXP register. |
| * Arguments : uint16_t data : Setting value |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_write_pipemaxp(uint16_t data) |
| { |
| rx65n_usbhost_putreg(data, RX65N_USB_PIPEMAXP); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_write_pipeperi |
| * Description : Specified data is written to PIPEPERI register. |
| * Arguments : uint16_t data : Setting value |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_write_pipeperi(uint16_t data) |
| { |
| rx65n_usbhost_putreg(data, RX65N_USB_PIPEPERI); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_read_pipectr |
| * Description : Returns DCPCTR or the specified pipe's PIPECTR register |
| * : content. The Pipe Control Register returned is |
| * : determined by the specified pipe number. |
| * Arguments : uint16_t pipeno : Pipe number. |
| * Return value : PIPExCTR content |
| ****************************************************************************/ |
| |
| static uint16_t hw_usb_read_pipectr(uint16_t pipeno) |
| { |
| volatile uint16_t *p_reg; |
| |
| /* Control pipe */ |
| |
| if (pipeno == 0) |
| { |
| p_reg = (uint16_t *)RX65N_USB_DCPCTR; |
| } |
| else |
| { |
| p_reg = (uint16_t *)(RX65N_USB_PIPE1CTR + (pipeno - 1)); |
| } |
| |
| return rx65n_usbhost_getreg(p_reg); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_write_pipectr |
| * Description : Specified data is written to the specified pipe's |
| * : PIPEPERI register. |
| * Arguments : uint16_t pipeno : Pipe number |
| * : uint16_t data : Setting value |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_write_pipectr(uint16_t pipeno, uint16_t data) |
| { |
| volatile uint16_t *p_reg; |
| |
| if (USB_PIPE0 == pipeno) |
| { |
| p_reg = (uint16_t *)RX65N_USB_DCPCTR; |
| } |
| else |
| { |
| p_reg = (uint16_t *)RX65N_USB_PIPE1CTR + (pipeno - 1); |
| } |
| |
| *p_reg = data; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_set_aclrm |
| * Description : The ACLRM-bit (Auto Buffer Clear Mode) is set in the |
| * : specified pipe's control register. |
| * Arguments : uint16_t pipeno : Pipe number |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_set_aclrm(uint16_t pipeno) |
| { |
| volatile uint16_t *p_reg; |
| if (USB_PIPE0 == pipeno) |
| { |
| p_reg = (uint16_t *)RX65N_USB_DCPCTR; |
| } |
| else |
| { |
| p_reg = (uint16_t *)RX65N_USB_PIPE1CTR + (pipeno - 1); |
| } |
| |
| (*p_reg) |= RX65N_USB_PIPECTR_ACLRM; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_clear_aclrm |
| * Description : Clear the ACLRM bit in the specified pipe's control |
| * : register to disable Auto Buffer Clear Mode. |
| * : its BEMP interrupt status. |
| * Arguments : usb_utr_t *ptr : Pointer to usb_utr_t structure. |
| * : uint16_t pipeno : Pipe number |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_clear_aclrm(uint16_t pipeno) |
| { |
| volatile uint16_t *p_reg; |
| if (USB_PIPE0 == pipeno) |
| { |
| p_reg = (uint16_t *)RX65N_USB_DCPCTR; |
| } |
| else |
| { |
| p_reg = (uint16_t *)RX65N_USB_PIPE1CTR + (pipeno - 1); |
| } |
| |
| (*p_reg) &= (~RX65N_USB_PIPECTR_ACLRM); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_set_sqclr |
| * Description : The SQCLR-bit (Toggle Bit Clear) is set in the specified |
| * : pipe's control register. Setting SQSET to 1 makes DATA0 |
| * : the expected data in the pipe's next transfer. |
| * Arguments : uint16_t pipeno : Pipe number |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_set_sqclr(uint16_t pipeno) |
| { |
| volatile uint16_t *p_reg; |
| if (USB_PIPE0 == pipeno) |
| { |
| p_reg = (uint16_t *)RX65N_USB_DCPCTR; |
| } |
| else |
| { |
| p_reg = (uint16_t *)RX65N_USB_PIPE1CTR + (pipeno - 1); |
| } |
| |
| (*p_reg) |= RX65N_USB_PIPECTR_SQCLR; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_set_sqset |
| * Description : The SQSET-bit (Toggle Bit Set) is set in the specified |
| * : pipe's control register. Setting SQSET to 1 makes DATA1 |
| * : the expected data in the next transfer. |
| * Arguments : usb_utr_t *ptr : Pointer to usb_utr_t structure. |
| * : uint16_t pipeno : Pipe number |
| * Return value : none |
| ****************************************************************************/ |
| |
| void hw_usb_set_sqset(uint16_t pipeno) |
| { |
| volatile uint16_t *p_reg; |
| if (USB_PIPE0 == pipeno) |
| { |
| p_reg = (uint16_t *)RX65N_USB_DCPCTR; |
| } |
| else |
| { |
| p_reg = (uint16_t *)RX65N_USB_PIPE1CTR + (pipeno - 1); |
| } |
| |
| (*p_reg) |= RX65N_USB_PIPECTR_SQSET; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_set_pid |
| * Description : Set the specified PID of the specified pipe's |
| * : DCPCTR/PIPECTR register. |
| * Arguments : uint16_t pipeno : Pipe number |
| * : uint16_t data : NAK/BUF/STALL. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_set_pid(uint16_t pipeno, uint16_t data) |
| { |
| volatile uint16_t *p_reg; |
| if (USB_PIPE0 == pipeno) |
| { |
| p_reg = (uint16_t *)RX65N_USB_DCPCTR; |
| } |
| else |
| { |
| p_reg = (uint16_t *)RX65N_USB_PIPE1CTR + (pipeno - 1); |
| } |
| |
| (*p_reg) &= (~RX65N_USB_PIPECTR_PID_MASK); |
| (*p_reg) |= data; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_clear_pid |
| * Description : Clear the specified PID-bits of the specified pipe's |
| * : DCPCTR/PIPECTR register. |
| * Arguments : uint16_t pipeno : Pipe number |
| * : uint16_t data : NAK/BUF/STALL - to be cleared. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_clear_pid(uint16_t pipeno, uint16_t data) |
| { |
| volatile uint16_t *p_reg; |
| if (USB_PIPE0 == pipeno) |
| { |
| p_reg = (uint16_t *)RX65N_USB_DCPCTR; |
| } |
| else |
| { |
| p_reg = (uint16_t *)RX65N_USB_PIPE1CTR + (pipeno - 1); |
| } |
| |
| (*p_reg) &= (~RX65N_USB_PIPECTR_PID_MASK); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_set_trenb |
| * Description : The TRENB-bit (Transaction Counter Enable) is set in |
| * : the specified pipe's control register, to enable the |
| * : counter. |
| * Arguments : uint16_t pipeno : Pipe number |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_set_trenb(uint16_t pipeno) |
| { |
| volatile uint16_t *p_reg; |
| |
| p_reg = (uint16_t *)RX65N_USB_PIPE1TRE + ((pipeno - 1) * 2); |
| |
| (*p_reg) |= RX65N_USB_PIPETRE_TRENB; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_clear_trenb |
| * Description : The TRENB-bit (Transaction Counter Enable) is cleared in |
| * : the specified pipe's control register, to disable the |
| * : counter. |
| * Arguments : uint16_t pipeno : Pipe number |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_clear_trenb(uint16_t pipeno) |
| { |
| volatile uint16_t *p_reg; |
| |
| p_reg = (uint16_t *)RX65N_USB_PIPE1TRE + ((pipeno - 1) * 2); |
| |
| (*p_reg) &= (~RX65N_USB_PIPETRE_TRENB); |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_set_trclr |
| * Description : The TRENB-bit (Transaction Counter Clear) is set in the |
| * : specified pipe's control register to clear the current |
| * : counter value. |
| * Arguments : uint16_t pipeno : Pipe number |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_set_trclr(uint16_t pipeno) |
| { |
| volatile uint16_t *p_reg; |
| |
| p_reg = (uint16_t *)RX65N_USB_PIPE1TRE + ((pipeno - 1) * 2); |
| (*p_reg) |= RX65N_USB_PIPETRE_TRCLR; |
| } |
| |
| /**************************************************************************** |
| * Function Name : hw_usb_write_pipetrn |
| * Description : Specified data is written to the specified pipe's |
| * : PIPETRN register. |
| * Arguments : uint16_t pipeno : Pipe number |
| * : uint16_t data : The value to write. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void hw_usb_write_pipetrn(uint16_t pipeno, uint16_t data) |
| { |
| volatile uint16_t *p_reg; |
| |
| p_reg = (uint16_t *)RX65N_USB_PIPE1TRN + ((pipeno - 1) * 2); |
| |
| *p_reg = data; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_bchg_enable |
| * Description : Enable BCHG interrupt for the specified USB port. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_bchg_enable(void) |
| { |
| hw_usb_hclear_sts_bchg(); |
| hw_usb_hset_enb_bchge(); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_bchg_disable |
| * Description : Disable BCHG interrupt for specified USB port. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_bchg_disable(void) |
| { |
| hw_usb_hclear_sts_bchg(); |
| hw_usb_hclear_enb_bchge(); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_set_uact |
| * Description : Start sending SOF to the connected USB device. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_set_uact(void) |
| { |
| hw_usb_rmw_dvstctr(RX65N_USB_DVSTCTR0_UACT, |
| ((RX65N_USB_DVSTCTR0_USBRST | |
| RX65N_USB_DVSTCTR0_RESUME) | |
| RX65N_USB_DVSTCTR0_UACT)); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_attch_enable |
| * Description : Enable ATTCH (attach) interrupt of the specified USB |
| * : port. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_attch_enable(void) |
| { |
| /* ATTCH status Clear */ |
| |
| hw_usb_hclear_sts_attch(); |
| |
| /* Attach enable */ |
| |
| hw_usb_hset_enb_attche(); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_attch_disable |
| * Description : Disable ATTCH (attach) interrupt of the specified USB |
| * : port. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_attch_disable(void) |
| { |
| /* ATTCH Clear */ |
| |
| hw_usb_hclear_sts_attch(); |
| |
| /* Attach disable */ |
| |
| hw_usb_hclear_enb_attche(); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_dtch_enable |
| * Description : Enable DTCH (detach) interrupt of the specified USB |
| * : port. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_dtch_enable(void) |
| { |
| /* DTCH Clear */ |
| |
| hw_usb_hclear_sts_dtch(); |
| |
| /* Detach enable */ |
| |
| hw_usb_hset_enb_dtche(); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_dtch_disable |
| * Description : Disable DTCH (detach) interrupt of the specified USB |
| * : port. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_dtch_disable(void) |
| { |
| /* DTCH Clear */ |
| |
| hw_usb_hclear_sts_dtch(); |
| |
| /* Detach disable */ |
| |
| hw_usb_hclear_enb_dtche(); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_berne_enable |
| * Description : Enable BRDY/NRDY/BEMP interrupt. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_berne_enable(void) |
| { |
| /* Enable BEMP, NRDY, BRDY */ |
| |
| hw_usb_set_intenb((RX65N_USB_INTENB0_BEMPE | |
| RX65N_USB_INTENB0_NRDYE) | |
| RX65N_USB_INTENB0_BRDYE); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_do_sqtgl |
| * Description : Toggle setting of the toggle-bit for the specified pipe |
| * : by argument. |
| * Arguments : uint16_t pipe : Pipe number. |
| * : uint16_t toggle : Current toggle status. |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_do_sqtgl(uint16_t pipe, uint16_t toggle) |
| { |
| if (USB_MAX_PIPE_NO < pipe) |
| { |
| return; /* Error */ |
| } |
| |
| /* Check toggle */ |
| |
| if (RX65N_USB_DCPCTR_SQMON == (toggle & RX65N_USB_DCPCTR_SQMON)) |
| { |
| /* Do pipe SQSET */ |
| |
| hw_usb_set_sqset(pipe); |
| } |
| else |
| { |
| /* Do pipe SQCLR */ |
| |
| hw_usb_set_sqclr(pipe); |
| } |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_write_data_control_pipe |
| * Description : Request the USB FIFO to write data, and manage the size |
| * : of written data. |
| * Arguments : uint8_t buf_add : Buffer address |
| * : size_t buf_size : Size of the data |
| * Return value : uint16_t end_flag |
| ****************************************************************************/ |
| |
| uint16_t usb_hstd_write_data_control_pipe(uint8_t * buf_add, |
| size_t buf_size) |
| { |
| uint16_t size; |
| uint16_t count = 0; |
| uint16_t buffer; |
| uint16_t mxps; |
| uint16_t end_flag; |
| |
| buffer = usb_cstd_is_set_frdy(USB_PIPE0, |
| RX65N_USB_USING_CFIFO, |
| RX65N_USB_CFIFOSEL_ISEL); |
| |
| /* Check error */ |
| |
| if (USB_FIFOERROR == buffer) |
| { |
| /* FIFO access error */ |
| |
| return USB_FIFOERROR; |
| } |
| |
| /* Data buffer size */ |
| |
| size = buf_size; |
| |
| /* Max Packet Size */ |
| |
| mxps = usb_cstd_get_maxpacket_size(USB_PIPE0); |
| |
| /* Data size check */ |
| |
| if (buf_size <= (uint32_t)size) |
| { |
| count = buf_size; |
| |
| /* Data count check */ |
| |
| if (0 == count) |
| { |
| end_flag = USB_WRITESHRT; |
| } |
| else if (0 != (count % mxps)) |
| { |
| /* Short Packet is end of write */ |
| |
| end_flag = USB_WRITESHRT; |
| } |
| else |
| { |
| end_flag = USB_WRITING; |
| } |
| } |
| else |
| { |
| /* Write continues */ |
| |
| end_flag = USB_WRITING; |
| count = mxps; |
| } |
| |
| buf_add = usb_hstd_write_fifo(count, USB_CUSE, buf_add); |
| g_rx65n_edlist[USB_PIPE0].xfrinfo->xfrd += count; |
| |
| /* Check data count to remain */ |
| |
| /* Check if this affects any of the standard requrest commands... */ |
| |
| if (buf_size <= (uint32_t) size) |
| { |
| /* Clear data count */ |
| |
| buffer = hw_usb_read_fifoctr(USB_CUSE); /* Read CFIFOCTR */ |
| |
| /* Check BVAL */ |
| |
| if (0u == (buffer & USB_BVAL)) |
| { |
| /* Short Packet */ |
| |
| hw_usb_set_bval(USB_CUSE); |
| } |
| } |
| |
| return end_flag; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_write_data |
| * Description : Switch PIPE, request the USB FIFO to write data, and |
| * : manage the size of written data. |
| * Arguments : uint16_t pipe : Pipe no. |
| * : uint16_t pipemode : CUSE/D0DMA/D1DMA |
| * Return value : uint16_t end_flag |
| ****************************************************************************/ |
| |
| uint16_t usb_hstd_write_data(uint8_t *buf_add, size_t buf_size, |
| uint16_t pipe, uint16_t pipemode) |
| { |
| uint16_t size; |
| uint16_t count = 0; |
| uint16_t buffer; |
| uint16_t mxps; |
| uint16_t end_flag; |
| |
| if (USB_MAX_PIPE_NO < pipe) |
| { |
| return USB_WRITESHRT; /* Error */ |
| } |
| |
| /* Changes FIFO port by the pipe. */ |
| |
| buffer = usb_cstd_is_set_frdy(pipe, pipemode, USB_FALSE); |
| |
| /* Check error */ |
| |
| if (USB_FIFOERROR == buffer) |
| { |
| /* FIFO access error */ |
| |
| return USB_FIFOERROR; |
| } |
| |
| /* Data buffer size */ |
| |
| size = usb_cstd_get_buf_size(pipe); |
| |
| /* Max Packet Size */ |
| |
| mxps = usb_cstd_get_maxpacket_size(pipe); |
| |
| /* Data size check */ |
| |
| if (buf_size <= (uint32_t)size) |
| { |
| count = buf_size; |
| |
| /* Data count check */ |
| |
| if (0 == count) |
| { |
| end_flag = USB_WRITESHRT; |
| } |
| else if (0 != (count % mxps)) |
| { |
| /* Short Packet is end of write */ |
| |
| end_flag = USB_WRITESHRT; |
| } |
| else |
| { |
| { |
| /* Write continues */ |
| |
| end_flag = USB_WRITEEND; |
| } |
| } |
| } |
| else |
| { |
| /* Write continues */ |
| |
| end_flag = USB_WRITING; |
| count = mxps; |
| } |
| |
| buf_add = usb_hstd_write_fifo(count, pipemode, buf_add); |
| g_rx65n_edlist[pipe].xfrinfo->xfrd += count; |
| g_rx65n_edlist[pipe].xfrinfo->buffer += count; |
| |
| /* Check data count to remain */ |
| |
| if (buf_size <= (uint32_t) size) |
| { |
| /* Clear data count */ |
| |
| buffer = hw_usb_read_fifoctr(pipemode); /* Read CFIFOCTR */ |
| |
| /* Check BVAL */ |
| |
| if (0u == (buffer & USB_BVAL)) |
| { |
| /* Short Packet */ |
| |
| hw_usb_set_bval(pipemode); |
| } |
| } |
| |
| return end_flag; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_receive_start |
| * Description : Start data reception using CPU/DMA transfer to USB |
| * : Host/USB device. |
| * Arguments : uint16_t pipe : Pipe no. |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_receive_start(uint8_t *buffer, size_t buflen, uint8_t pipe) |
| { |
| uint32_t length; |
| uint16_t mxps; |
| uint16_t useport; |
| |
| if (USB_MAX_PIPE_NO < pipe) |
| { |
| return; /* Error */ |
| } |
| |
| /* Select NAK */ |
| |
| usb_cstd_set_nak(pipe); |
| |
| /* Ignore count clear */ |
| |
| hw_usb_clear_status_bemp(pipe); /* BEMP Status Clear */ |
| hw_usb_clear_sts_brdy(pipe); /* BRDY Status Clear */ |
| hw_usb_clear_status_nrdy(pipe); /* NRDY Status Clear */ |
| nrdy_retries[pipe] = 0; /* Initialize theh NRDY retries to 0 */ |
| useport = USB_CUSE; |
| |
| /* Changes the FIFO port by the pipe. */ |
| |
| usb_cstd_chg_curpipe(pipe, useport, USB_FALSE); |
| mxps = usb_cstd_get_maxpacket_size(pipe); /* Max Packet Size */ |
| |
| length = buflen; |
| |
| if (0u != length) |
| { |
| /* Data length check */ |
| |
| if ((0u == (length % mxps)) |
| { |
| /* Set Transaction counter */ |
| |
| usb_cstd_set_transaction_counter(pipe, length / mxps); |
| } |
| else |
| { |
| /* Set Transaction counter */ |
| |
| usb_cstd_set_transaction_counter(pipe, length / mxps + 1u)); |
| } |
| } |
| |
| usb_cstd_set_buf(pipe); /* Set BUF */ |
| |
| /* Enable Ready Interrupt */ |
| |
| hw_usb_set_brdyenb(pipe); |
| |
| /* Enable Not Ready Interrupt */ |
| |
| usb_cstd_nrdy_enable(pipe); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_send_start |
| * Description : Start data transmission using CPUtransfer to USB host/ |
| * : /device. |
| * Arguments : uint8_t pipe : Pipe no. |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_send_start(uint8_t *buffer, size_t buflen, uint8_t pipe) |
| { |
| uint16_t useport; |
| |
| if (USB_MAX_PIPE_NO < pipe) |
| { |
| return; /* Error */ |
| } |
| |
| usb_cstd_set_nak(pipe); /* Select NAK */ |
| hw_usb_clear_status_bemp(pipe); /* BEMP Status Clear */ |
| hw_usb_clear_sts_brdy(pipe); /* BRDY Status Clear */ |
| hw_usb_clear_status_nrdy(pipe); /* NRDY Status Clear */ |
| nrdy_retries[pipe] = 0; /* Initialize theh NRDY retries to 0 */ |
| |
| /* Pipe number to FIFO port select */ |
| |
| useport = USB_CUSE; |
| |
| /* Changes the FIFO port by the pipe. */ |
| |
| usb_cstd_chg_curpipe(pipe, useport, USB_FALSE); |
| |
| /* Buffer to FIFO data write */ |
| |
| usb_hstd_buf_to_fifo(buffer, buflen, pipe, useport); |
| usb_cstd_set_buf(pipe); /* Set BUF */ |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_get_pipe_no |
| * Description : Get PIPE No. |
| * Arguments : uint8_t type : Transfer Type.(USB_EP_BULK/USB_EP_INT) |
| * : uint8_t dir : (USB_PIPE_DIR_IN/USB_PIPE_DIR_OUT) |
| * Return value : Pipe no (USB_PIPE1->USB_PIPE9:OK, USB_NULL:Error) |
| ****************************************************************************/ |
| |
| uint8_t usb_hstd_get_pipe_no(uint8_t type, uint8_t dir) |
| { |
| uint8_t pipe_no = USB_NULL; |
| uint16_t pipe; |
| |
| if (USB_EP_BULK == type) |
| { |
| /* BULK PIPE Loop */ |
| |
| /* WAIT_LOOP */ |
| |
| if (dir == USB_EP_IN) |
| { |
| pipe = USB_BULK_PIPE_START; |
| } |
| else |
| { |
| pipe = USB_BULK_PIPE_START + 1; |
| } |
| |
| for (; pipe < (USB_BULK_PIPE_END + 1); pipe = pipe + 2) |
| { |
| if (USB_FALSE == g_usb_pipe_table[pipe].use_flag) |
| { |
| /* Check Free pipe */ |
| |
| pipe_no = pipe; /* Set Free pipe */ |
| break; |
| } |
| } |
| } |
| |
| if (USB_EP_INT == type) |
| { |
| /* Interrupt PIPE Loop */ |
| |
| /* WAIT_LOOP */ |
| |
| for (pipe = USB_INT_PIPE_START; pipe < (USB_INT_PIPE_END +1); pipe++) |
| { |
| if (USB_FALSE == g_usb_pipe_table[pipe].use_flag) |
| { |
| /* Check Free pipe */ |
| |
| pipe_no = pipe; /* Set Free pipe */ |
| break; |
| } |
| } |
| } |
| |
| return pipe_no; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_read_data_control_pipe |
| * Description : Request to read data from USB FIFO, and manage the size |
| * : of the data read. |
| * Arguments : None |
| * Return value : USB_READING / USB_READEND / USB_READSHRT / USB_READOVER |
| ****************************************************************************/ |
| |
| uint16_t usb_hstd_read_data_control_pipe(void) |
| { |
| uint16_t count; |
| uint16_t buffer; |
| uint16_t mxps; |
| uint16_t dtln; |
| uint16_t end_flag; |
| |
| /* Changes FIFO port by the pipe. */ |
| |
| buffer = usb_cstd_is_set_frdy(USB_PIPE0, USB_CUSE, USB_FALSE); |
| |
| if (USB_FIFOERROR == buffer) |
| { |
| /* FIFO access error */ |
| |
| return USB_FIFOERROR; |
| syslog(LOG_INFO, "FIFO ERROR"); |
| } |
| |
| dtln = buffer & RX65N_USB_FIFOCTR_DTLN; |
| |
| /* Max Packet Size */ |
| |
| mxps = usb_cstd_get_maxpacket_size(USB_PIPE0); |
| |
| /* now calculate the count */ |
| |
| count = g_rx65n_tdlist[USB_PIPE0].ed->xfrinfo->buflen - |
| g_rx65n_tdlist[USB_PIPE0].ed->xfrinfo->xfrd; |
| |
| if (count < dtln) |
| { |
| /* Buffer Over ? */ |
| |
| end_flag = USB_READOVER; |
| usb_cstd_set_nak(USB_PIPE0); /* Set NAK */ |
| count = g_rx65n_tdlist[USB_PIPE0].ed->xfrinfo->xfrd; |
| g_rx65n_tdlist[USB_PIPE0].ed->xfrinfo->xfrd = dtln; |
| } |
| |
| else if (count == dtln) |
| { |
| /* Just Receive Size */ |
| |
| count = dtln; |
| end_flag = USB_READEND; |
| usb_cstd_set_nak(USB_PIPE0); /* Set NAK */ |
| } |
| else |
| { |
| /* Continus Receive data */ |
| |
| count = dtln; |
| |
| end_flag = USB_READING; |
| if ((0 == count) || (0 != (count % mxps))) |
| { |
| /* Null Packet receive */ |
| |
| end_flag = USB_READSHRT; |
| usb_cstd_set_nak(USB_PIPE0); /* Select NAK */ |
| } |
| } |
| |
| if (0 == dtln) |
| { |
| /* 0 length packet */ |
| |
| /* Clear BVAL */ |
| |
| hw_usb_set_bclr(USB_CUSE); |
| } |
| else |
| { |
| g_rx65n_tdlist[USB_PIPE0].ed->xfrinfo->buffer = usb_hstd_read_fifo( |
| count, USB_CUSE, g_rx65n_tdlist[USB_PIPE0].ed->xfrinfo->buffer); |
| } |
| |
| g_rx65n_tdlist[USB_PIPE0].ed->xfrinfo->xfrd += count; |
| |
| return end_flag; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_read_data |
| * Description : Request to read data from USB FIFO, and manage the size |
| * : of the data read. |
| * Arguments : uint16_t pipe : Pipe no. |
| * : uint16_t pipemode : Pipe mode (CFIFO/D0FIFO/D1FIFO) |
| * Return value : USB_READING / USB_READEND / USB_READSHRT / USB_READOVER |
| ****************************************************************************/ |
| |
| uint16_t usb_hstd_read_data(uint16_t pipe, uint16_t pipemode) |
| { |
| uint16_t count; |
| uint16_t buffer; |
| uint16_t mxps; |
| uint16_t dtln; |
| uint16_t end_flag = 0; |
| |
| if (USB_MAX_PIPE_NO < pipe) |
| { |
| return USB_ERROR; /* Error */ |
| } |
| |
| if (pipe != 0) /* Data transfer for non CTRL pipe */ |
| { |
| buffer = usb_cstd_is_set_frdy(pipe, pipemode, USB_FALSE); |
| |
| if (USB_FIFOERROR == buffer) |
| { |
| return USB_FIFOERROR; |
| } |
| |
| dtln = buffer & RX65N_USB_FIFOCTR_DTLN; |
| mxps = usb_cstd_get_maxpacket_size(pipe); |
| |
| /* now calculate the count */ |
| |
| if (pipe == g_kbdpipe) |
| { |
| /* For HID KBD read the received data */ |
| |
| count = dtln; |
| } |
| else |
| { |
| count = g_rx65n_edlist[pipe].xfrinfo->buflen - |
| g_rx65n_edlist[pipe].xfrinfo->xfrd; |
| } |
| |
| if (count < dtln) |
| { |
| /* Buffer Over ? */ |
| |
| count = dtln; |
| end_flag = USB_READOVER; |
| } |
| |
| else if (count == dtln) |
| { |
| /* Just Receive Size */ |
| |
| count = dtln; |
| end_flag = USB_READEND; |
| usb_cstd_set_nak(pipe); |
| } |
| else |
| { |
| /* Continus Receive data */ |
| |
| count = dtln; |
| |
| end_flag = USB_READING; |
| if ((0 == count) || (0 != (count % mxps))) |
| { |
| /* Null Packet receive */ |
| |
| end_flag = USB_READSHRT; |
| } |
| } |
| |
| usb_hstd_read_fifo(count, |
| pipemode, g_rx65n_edlist[pipe].xfrinfo->buffer); |
| |
| g_rx65n_edlist[pipe].xfrinfo->xfrd += count; |
| |
| if (pipe == g_kbdpipe) |
| { |
| /* For HID KBD read pointer should not be incremented */ |
| } |
| else |
| { |
| g_rx65n_edlist[pipe].xfrinfo->buffer += count; |
| } |
| } |
| |
| return end_flag; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_data_end |
| * Description : Set USB registers as appropriate after data |
| * : transmission/reception, and call the callback function |
| * : as transmission/reception is complete. |
| * Arguments : uint16_t pipe : Pipe no. |
| * : uint16_t status : Transfer status type. |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_data_end(uint16_t pipe, uint16_t status) |
| { |
| if (USB_MAX_PIPE_NO < pipe) |
| { |
| return; /* Error */ |
| } |
| |
| /* PID = NAK */ |
| |
| /* Set NAK */ |
| |
| usb_cstd_set_nak(pipe); |
| |
| /* Disable Interrupt */ |
| |
| /* Disable Ready Interrupt */ |
| |
| hw_usb_clear_brdyenb(pipe); |
| |
| /* Disable Not Ready Interrupt */ |
| |
| hw_usb_clear_nrdyenb(pipe); |
| |
| /* Disable Empty Interrupt */ |
| |
| hw_usb_clear_bempenb(pipe); |
| |
| /* Disable Transaction count */ |
| |
| usb_cstd_clr_transaction_counter(pipe); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_buf_to_fifo |
| * Description : Set USB registers as required to write from data buffer |
| * : to USBFIFO, to have USB FIFO to write data to bus. |
| * Arguments : uint16_t pipe : Pipe no. |
| * : uint16_t useport : Port no. |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_buf_to_fifo(uint8_t *buffer, size_t buflen, uint16_t pipe, |
| uint16_t useport) |
| { |
| uint16_t end_flag; |
| |
| if (USB_MAX_PIPE_NO < pipe) |
| { |
| return; /* Error */ |
| } |
| |
| /* Disable Ready and empty Interrupt */ |
| |
| hw_usb_clear_brdyenb(pipe); |
| hw_usb_clear_bempenb(pipe); |
| |
| end_flag = usb_hstd_write_data(buffer, buflen, pipe, useport); |
| |
| switch (end_flag) |
| { |
| case USB_WRITING: |
| |
| /* Enable Ready Interrupt */ |
| |
| hw_usb_set_brdyenb(pipe); |
| |
| /* Enable Not Ready Interrupt */ |
| |
| usb_cstd_nrdy_enable(pipe); |
| |
| break; |
| |
| case USB_WRITEEND: |
| |
| /* End of data write */ |
| |
| /* continue */ |
| |
| case USB_WRITESHRT: |
| |
| /* End of data write */ |
| |
| /* Enable Empty Interrupt */ |
| |
| hw_usb_set_bempenb(pipe); |
| |
| /* Enable Not Ready Interrupt */ |
| |
| usb_cstd_nrdy_enable(pipe); |
| |
| break; |
| |
| case USB_FIFOERROR: |
| |
| /* FIFO access error */ |
| |
| syslog(LOG_INFO, "### FIFO access error\n"); |
| usb_hstd_forced_termination(pipe, USB_DATA_ERR); |
| break; |
| |
| default: |
| usb_hstd_forced_termination(pipe, USB_DATA_ERR); |
| break; |
| } |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_brdy_pipe_process |
| * Description : Search for the PIPE No. that BRDY interrupt occurred, |
| * : and request data transmission/reception from the PIPE |
| * Arguments : uint16_t bitsts : BRDYSTS Register & BRDYENB |
| * : Register |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_brdy_pipe_process(uint16_t bitsts) |
| { |
| uint16_t i; |
| uint16_t end_flag; |
| size_t data_len; |
| uint8_t *data_address; |
| uint16_t buffer; |
| #ifdef CONFIG_USBHOST_ASYNCH |
| struct rx65n_usbhost_s *priv = &g_usbhost; |
| #endif |
| |
| for (i = USB_MIN_PIPE_NO; i <= USB_MAX_PIPE_NO; i++) |
| { |
| if (0 != (bitsts & USB_BITSET(i))) |
| { |
| /* Clear the Interrupt status bit - clear for both BEMP and BRDY */ |
| |
| hw_usb_clear_sts_brdy(i); |
| hw_usb_clear_status_bemp(i); |
| if (RX65N_USB_PIPECFG_DIR == usb_cstd_get_pipe_dir(i)) |
| { |
| /* Buffer to FIFO data write */ |
| |
| data_len = g_rx65n_edlist[i].xfrinfo->buflen - |
| g_rx65n_edlist[i].xfrinfo->xfrd; |
| buffer = usb_cstd_get_pid(i); |
| |
| /* MAX packet size error ? */ |
| |
| if (RX65N_USB_DCPCTR_PIDSTALL == (buffer & |
| RX65N_USB_DCPCTR_PIDSTALL)) |
| { |
| syslog(LOG_INFO, "### STALL Pipe %d\n", i); |
| usb_hstd_forced_termination(i, USB_DATA_STALL); |
| } |
| else |
| { |
| if (data_len == 0) |
| { |
| syslog(LOG_INFO, "BRDY can NOT be with 0 len\ |
| data for pipe\n", i); |
| } |
| |
| /* If still data is present - let the data |
| * transfer coninue |
| * |
| */ |
| |
| else |
| { |
| data_address = g_rx65n_edlist[i].xfrinfo->buffer; |
| usb_hstd_buf_to_fifo(data_address, data_len, i, |
| USB_CUSE); |
| usb_cstd_set_buf(i); /* Set BUF */ |
| } |
| } |
| } |
| else |
| { |
| /* FIFO to Buffer data read */ |
| |
| end_flag = usb_hstd_read_data(i, USB_CUSE); |
| |
| if (end_flag != USB_READING) |
| { |
| #ifdef CONFIG_USBHOST_ASYNCH |
| if ((i == g_kbdpipe) && |
| (NULL == g_rx65n_edlist[i].xfrinfo->callback)) |
| #else |
| if (i == g_kbdpipe) |
| #endif |
| { |
| usb_cstd_clr_stall(i); |
| } |
| |
| else |
| { |
| usb_hstd_data_end(i, USB_DATA_OK); |
| g_rx65n_edlist[i].xfrinfo->tdstatus = |
| TD_CC_NOERROR; |
| |
| #ifdef CONFIG_USBHOST_ASYNCH |
| if ((g_rx65n_edlist[i].xfrinfo != 0) |
| && (g_rx65n_edlist[i].xfrinfo->callback)) |
| { |
| hw_usb_write_dcpmxps(USB_DEFPACKET + USB_DEVICE_1); |
| rx65n_usbhost_asynch_completion(priv, |
| &g_rx65n_edlist[i]); |
| } |
| |
| #endif |
| } |
| |
| nxsem_post(&g_rx65n_edlist[i].wdhsem); |
| } |
| } |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_nrdy_pipe_process |
| * Description : Search for PIPE No. that occurred NRDY interrupt, and |
| * : execute the process for PIPE when NRDY interrupt |
| * : occurred |
| * Arguments : uint16_t bitsts : NRDYSTS Register & NRDYENB Register |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_nrdy_pipe_process(uint16_t bitsts) |
| { |
| uint16_t i; |
| |
| /* WAIT_LOOP */ |
| |
| for (i = USB_MIN_PIPE_NO; i <= USB_MAX_PIPE_NO; i++) |
| { |
| if (0 != (bitsts & USB_BITSET(i))) |
| { |
| hw_usb_clear_status_nrdy(i); |
| usb_hstd_nrdy_endprocess(i); |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_bemp_pipe_process |
| * Description : Search for PIPE No. that BEMP interrupt occurred, and |
| * : complete data transmission for the PIPE |
| * Arguments : uint16_t bitsts : BEMPSTS Register & BEMPENB Register |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_bemp_pipe_process(uint16_t bitsts) |
| { |
| uint16_t buffer; |
| uint16_t i; |
| size_t data_len; |
| |
| /* WAIT_LOOP */ |
| |
| for (i = USB_MIN_PIPE_NO; i <= USB_PIPE5; i++) |
| { |
| if (0 != (bitsts & USB_BITSET(i))) |
| { |
| /* Clear the Interrupt status bit */ |
| |
| hw_usb_clear_status_bemp(i); |
| hw_usb_clear_sts_brdy(i); |
| |
| data_len = g_rx65n_edlist[i].xfrinfo->buflen - |
| g_rx65n_edlist[i].xfrinfo->xfrd; |
| buffer = usb_cstd_get_pid(i); |
| |
| /* MAX packet size error ? */ |
| |
| if (RX65N_USB_DCPCTR_PIDSTALL == (buffer & |
| RX65N_USB_DCPCTR_PIDSTALL)) |
| { |
| syslog(LOG_INFO, "### STALL Pipe %d\n", i); |
| usb_hstd_forced_termination(i, USB_DATA_STALL); |
| } |
| else |
| { |
| if (data_len == 0) |
| { |
| usb_hstd_data_end(i, USB_DATA_OK); |
| g_rx65n_edlist[i].xfrinfo->tdstatus = TD_CC_NOERROR; |
| |
| /* Release the semaphore for this pipe */ |
| |
| nxsem_post(&g_rx65n_edlist[i].wdhsem); |
| } |
| |
| else |
| { |
| syslog(LOG_INFO, |
| "BEMP can NOT be with %d len data for pipe\n", |
| data_len, i); |
| } |
| } |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_get_pipe_peri_value |
| * Description : Get value to be set in PIPEPERI |
| * Arguments : uint8_t binterval : bInterval for ENDPOINT Descriptor |
| * Return value : Value for set PIPEPERI |
| ****************************************************************************/ |
| |
| uint16_t usb_hstd_get_pipe_peri_value(uint8_t binterval) |
| { |
| uint16_t pipe_peri = USB_NULL; |
| uint16_t work1; |
| uint16_t work2; |
| |
| /* Set interval counter */ |
| |
| if (0 != binterval) |
| { |
| /* FS/LS interrupt */ |
| |
| /* The time of the FS/LS interrupt forwarding of the interval is |
| * specified by the unit of ms. It is necessary to calculate to |
| * specify USB-IP by the n-th power of two. NAK rate of the control |
| * and the bulk transfer doesn't correspond. |
| */ |
| |
| work1 = binterval; |
| work2 = 0; |
| for (; work1 != 0; work2++ ) |
| { |
| work1 >>= 1; |
| } |
| |
| if (0 != work2) |
| { |
| /* Interval time */ |
| |
| pipe_peri |= (work2 - 1); |
| } |
| } |
| |
| return pipe_peri; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_chk_attach |
| * Description : Checks whether USB Device is attached or not and return |
| * : USB speed of USB Device |
| * Arguments : none |
| * Return value : uint16_t : connection status |
| * : : (USB_ATTACHF/USB_ATTACHL/USB_DETACH/USB_OK) |
| * Note : Please change for your SYSTEM |
| ****************************************************************************/ |
| |
| uint16_t usb_hstd_chk_attach(void) |
| { |
| uint16_t buf[3]; |
| usb_hstd_read_lnst(buf); |
| |
| if (0 == (buf[1] & RX65N_USB_DVSTCTR0_RHST)) |
| { |
| if (RX65N_USB_SYSSTS0_LNST_FS_JSTS == (buf[0] & 3)) |
| { |
| /* High/Full speed device */ |
| |
| return USB_ATTACHF; |
| } |
| else if (RX65N_USB_SYSSTS0_LNST_LS_JSTS == (buf[0] & 3)) |
| { |
| /* Low speed device */ |
| |
| return USB_ATTACHL; |
| } |
| else if (RX65N_USB_SYSSTS0_LNST_SE0 == (buf[0] & 3)) |
| { |
| syslog(LOG_INFO, "Debug: Detach device\n"); |
| } |
| else |
| { |
| syslog(LOG_INFO, "Attach unknown speed device\n"); |
| } |
| } |
| else |
| { |
| syslog(LOG_INFO, "Already device attached\n"); |
| return 0; |
| } |
| |
| return USB_DETACH; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_chk_clk |
| * Description : Checks SOF sending setting when USB Device is detached |
| * : or suspended , BCHG interrupt enable setting and |
| *: clock stop processing |
| * Arguments : uint16_t event : device state |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_chk_clk(uint16_t event) |
| { |
| if ((USB_DETACH == event) || (USB_SUSPEND == event)) |
| { |
| usb_hstd_chk_sof(); |
| |
| /* Enable port BCHG interrupt */ |
| |
| usb_hstd_bchg_enable(); |
| } |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_detach_process |
| * Description : Handles the require processing when USB device is |
| * : detached (Data transfer forcibly termination processing |
| *: to the connected USB Device, the clock supply stop setting and |
| *: the USB interrupt disable setting etc) |
| * Arguments : none |
| * Return value : Device speed |
| ****************************************************************************/ |
| |
| uint16_t usb_hstd_detach_process(void) |
| { |
| uint16_t connect_inf; |
| uint16_t i; |
| |
| /* ATTCH interrupt disable */ |
| |
| usb_hstd_attch_disable(); |
| |
| /* DTCH interrupt disable */ |
| |
| usb_hstd_dtch_disable(); |
| usb_hstd_bchg_disable(); |
| |
| /* Check if control pipe in use */ |
| |
| if (RX65N_USB_PIPECTR_PIDBUF == usb_cstd_get_pid(USB_PIPE0)) |
| { |
| /* End of data transfer (IN/OUT) */ |
| |
| usb_hstd_forced_termination(USB_PIPE0, USB_DATA_STOP); |
| } |
| |
| usb_cstd_clr_pipe_cnfg(USB_PIPE0); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = USB_MIN_PIPE_NO; i <= USB_MAX_PIPE_NO; i++) |
| { |
| /* Not control transfer */ |
| |
| /* Is this pipe used */ |
| |
| if (g_usb_pipe_table[i].use_flag == USB_TRUE) |
| { |
| /* PID=BUF ? */ |
| |
| if (RX65N_USB_PIPECTR_PIDBUF == usb_cstd_get_pid(i)) |
| { |
| /* End of data transfer (IN/OUT) */ |
| |
| usb_hstd_forced_termination(i, USB_DATA_STOP); |
| } |
| |
| usb_cstd_clr_pipe_cnfg(i); |
| } |
| } |
| |
| /* Decide USB Line state (ATTACH) */ |
| |
| connect_inf = usb_hstd_chk_attach(); |
| switch (connect_inf) |
| { |
| case USB_ATTACHL: |
| usb_hstd_attach(connect_inf); |
| break; |
| |
| case USB_ATTACHF: |
| usb_hstd_attach(connect_inf); |
| break; |
| |
| case USB_DETACH: |
| |
| /* USB detach */ |
| |
| usb_hstd_detach(); |
| |
| /* Check clock */ |
| |
| usb_hstd_chk_clk(USB_DETACH); |
| break; |
| |
| default: |
| |
| /* USB detach */ |
| |
| usb_hstd_detach(); |
| |
| /* Check clock */ |
| |
| usb_hstd_chk_clk(USB_DETACH); |
| break; |
| } |
| |
| return connect_inf; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_read_lnst |
| * Description : Reads LNST register two times, checks whether these |
| * : values are equal and returns the value of DVSTCTR |
| * : register that correspond to the port specified by 2nd |
| * : argument. |
| * Arguments : uint16_t *buf : Pointer to the buffer to store DVSTCTR |
| * : register |
| * Return value : none |
| * Note : Please change for your SYSTEM |
| ****************************************************************************/ |
| |
| void usb_hstd_read_lnst(uint16_t *buf) |
| { |
| /* WAIT_LOOP */ |
| |
| if (g_attached) |
| { |
| do |
| { |
| buf[0] = hw_usb_read_syssts(); |
| |
| /* 30ms wait */ |
| |
| up_mdelay(30); |
| buf[1] = hw_usb_read_syssts(); |
| if ((buf[0] & RX65N_USB_SYSSTS0_LNST) == |
| (buf[1] & RX65N_USB_SYSSTS0_LNST)) |
| { |
| /* 20ms wait */ |
| |
| up_mdelay(20); |
| buf[1] = hw_usb_read_syssts(); |
| } |
| } |
| while (((buf[0] & RX65N_USB_SYSSTS0_LNST) != |
| (buf[1] & RX65N_USB_SYSSTS0_LNST)) || (((buf[0] & 3) == 0))); |
| buf[1] = hw_usb_read_dvstctr(); |
| } |
| |
| if (g_detached) |
| { |
| do |
| { |
| buf[0] = hw_usb_read_syssts(); |
| |
| /* 30ms wait */ |
| |
| up_mdelay(30); |
| buf[1] = hw_usb_read_syssts(); |
| if ((buf[0] & RX65N_USB_SYSSTS0_LNST) == |
| (buf[1] & RX65N_USB_SYSSTS0_LNST)) |
| { |
| /* 20ms wait */ |
| |
| up_mdelay(20); |
| buf[1] = hw_usb_read_syssts(); |
| } |
| } |
| while (((buf[0] & RX65N_USB_SYSSTS0_LNST) != |
| (buf[1] & RX65N_USB_SYSSTS0_LNST))); |
| buf[1] = hw_usb_read_dvstctr(); |
| } |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_attach_process |
| * Description : Interrupt disable setting when USB Device is attached |
| * : handles the required interrupt disable setting etc when |
| * : USB device is attached. |
| * Arguments : none |
| * Return value : Speed of the device |
| * Note : Please change for your SYSTEM |
| ****************************************************************************/ |
| |
| uint16_t usb_hstd_attach_process(void) |
| { |
| uint16_t connect_inf; |
| |
| /* ATTCH interrupt disable */ |
| |
| usb_hstd_attch_disable(); |
| |
| /* DTCH interrupt disable */ |
| |
| usb_hstd_dtch_disable(); |
| usb_hstd_bchg_disable(); |
| |
| /* Decide USB Line state (ATTACH) */ |
| |
| connect_inf = usb_hstd_chk_attach(); |
| switch (connect_inf) |
| { |
| case USB_ATTACHL: |
| usb_hstd_attach(connect_inf); |
| break; |
| |
| case USB_ATTACHF: |
| usb_hstd_attach(connect_inf); |
| break; |
| |
| case USB_DETACH: |
| |
| /* USB detach */ |
| |
| usb_hstd_detach(); |
| |
| /* Check clock */ |
| |
| usb_hstd_chk_clk(USB_DETACH); |
| break; |
| |
| default: |
| usb_hstd_attach(USB_ATTACHF); |
| break; |
| } |
| |
| return connect_inf; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_chk_sof |
| * Description : Checks whether SOF is sended or not |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_chk_sof(void) |
| { |
| up_mdelay(1); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_bus_reset |
| * Description : Setting USB register when BUS Reset |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_bus_reset(void) |
| { |
| uint16_t buf; |
| uint16_t i; |
| |
| /* USBRST=1, UACT=0 */ |
| |
| hw_usb_rmw_dvstctr(RX65N_USB_DVSTCTR0_USBRST, |
| (RX65N_USB_DVSTCTR0_USBRST | RX65N_USB_DVSTCTR0_UACT)); |
| |
| /* Wait 50ms */ |
| |
| up_mdelay(50); /* usb_cpu_delay_xms(50); */ |
| |
| /* USBRST=0, RESUME=0, UACT=1 */ |
| |
| usb_hstd_set_uact(); |
| |
| /* Wait 10ms or more (USB reset recovery) */ |
| |
| up_mdelay(20); /* usb_cpu_delay_xms(20); */ |
| |
| /* WAIT_LOOP */ |
| |
| /* If DVSTCTR0 last 3 bits i.e. RHST bit fields are 1xx. |
| * Then the USB reset is still in progress |
| */ |
| |
| for (i = 0, buf = 0x04; (i < 3) && (0x04 == buf); ++i) |
| { |
| /* DeviceStateControlRegister - ResetHandshakeStatusCheck */ |
| |
| buf = hw_usb_read_dvstctr() & RX65N_USB_DVSTCTR0_RHST; |
| if (0x04 == buf) |
| { |
| /* Wait */ |
| |
| up_mdelay(10); |
| } |
| } |
| |
| /* 30ms wait */ |
| |
| up_mdelay(30); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_write_fifo |
| * Description : Write specified amount of data to specified USB FIFO. |
| * Arguments : uint16_t count : Write size |
| * : uint16_t pipemode : The mode of CPU/DMA(D0)/DMA(D1). |
| * : uint16_t *write_p : Address of buffer of data to write |
| * Return value : The incremented address of last argument (write_p). |
| ****************************************************************************/ |
| |
| uint8_t *usb_hstd_write_fifo(uint16_t count, uint16_t pipemode, |
| uint8_t *write_p) |
| { |
| uint16_t even; |
| { |
| /* WAIT_LOOP */ |
| |
| for (even = count >> 1; 0 != even; --even) |
| { |
| /* 16bit access */ |
| |
| hw_usb_write_fifo16(pipemode, *((uint16_t *)write_p)); |
| |
| /* Renewal write pointer */ |
| |
| write_p += sizeof(uint16_t); |
| } |
| |
| if ((count & 0x0001u) != 0u) |
| { |
| /* count == odd */ |
| |
| /* Change FIFO access width */ |
| |
| hw_usb_set_mbw(pipemode, USB_MBW_8); |
| |
| /* FIFO write */ |
| |
| hw_usb_write_fifo8(pipemode, *write_p); |
| |
| /* Return FIFO access width */ |
| |
| hw_usb_set_mbw(pipemode, USB_MBW_16); |
| |
| /* Renewal write pointer */ |
| |
| write_p++; |
| } |
| } |
| |
| return write_p; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_read_fifo |
| * Description : Read specified buffer size from the USB FIFO. |
| * Arguments : uint16_t count :Read size |
| * : uint16_t pipemode:The mode of CPU/DMA(D0)/DMA(D1). |
| * : uint16_t *read_p :Address of buffer to store the read |
| * : data |
| * Return value : Pointer to a buffer that contains the data to be read |
| * : next. |
| ****************************************************************************/ |
| |
| uint8_t *usb_hstd_read_fifo(uint16_t count, uint16_t pipemode, |
| uint8_t *read_p) |
| { |
| uint16_t even; |
| uint32_t odd_byte_data_temp; |
| |
| /* WAIT_LOOP */ |
| |
| for (even = count >> 1; 0 != even; --even) |
| { |
| /* 16bit FIFO access */ |
| |
| *(uint16_t *)read_p = hw_usb_read_fifo16(pipemode); |
| |
| /* Renewal read pointer */ |
| |
| read_p += sizeof(uint16_t); |
| } |
| |
| if ((count & 0x0001) != 0) |
| { |
| /* 16bit FIFO access */ |
| |
| odd_byte_data_temp = hw_usb_read_fifo16(pipemode); |
| |
| /* Condition compilation by the difference of the little endian */ |
| |
| *read_p = (uint8_t) (odd_byte_data_temp & 0x00ff); |
| |
| /* Renewal read pointer */ |
| |
| read_p += sizeof(uint8_t); |
| } |
| |
| return read_p; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_forced_termination |
| * Description : Terminate data transmission and reception. |
| * Arguments : uint16_t pipe : Pipe Number |
| * : uint16_t status : Transfer status type |
| * Return value : none |
| * Note : In the case of timeout status, it does not call |
| * : back. |
| ****************************************************************************/ |
| |
| void usb_hstd_forced_termination(uint16_t pipe, uint16_t status) |
| { |
| uint16_t buffer; |
| |
| /* PID = NAK */ |
| |
| /* Set NAK */ |
| |
| usb_cstd_set_nak(pipe); |
| |
| /* Disable Ready Interrupt */ |
| |
| hw_usb_clear_brdyenb(pipe); |
| |
| /* Disable Not Ready Interrupt */ |
| |
| hw_usb_clear_nrdyenb(pipe); |
| |
| /* Disable Empty Interrupt */ |
| |
| hw_usb_clear_bempenb(pipe); |
| |
| usb_cstd_clr_transaction_counter(pipe); |
| |
| /* Clear CFIFO-port */ |
| |
| buffer = hw_usb_read_fifosel(USB_CUSE); |
| if ((buffer & USB_CURPIPE) == pipe) |
| { |
| /* Changes the FIFO port by the pipe. */ |
| |
| usb_cstd_chg_curpipe(USB_PIPE0, USB_CUSE, USB_FALSE); |
| } |
| |
| #ifdef DMA_DTC_NOT_ENABLED |
| #if ((USB_CFG_DTC == USB_CFG_ENABLE) || (USB_CFG_DMA == USB_CFG_ENABLE)) |
| |
| /* Clear D0FIFO-port */ |
| |
| buffer = hw_usb_read_fifosel(ptr, USB_D0USE); |
| if ((buffer & USB_CURPIPE) == pipe) |
| { |
| /* Changes the FIFO port by the pipe. */ |
| |
| usb_cstd_chg_curpipe(ptr, USB_PIPE0, USB_D0USE, USB_FALSE); |
| } |
| |
| /* Clear D1FIFO-port */ |
| |
| buffer = hw_usb_read_fifosel(ptr, USB_D1USE); |
| if ((buffer & USB_CURPIPE) == pipe) |
| { |
| /* Changes the FIFO port by the pipe. */ |
| |
| usb_cstd_chg_curpipe(ptr, USB_PIPE0, USB_D1USE, USB_FALSE); |
| } |
| |
| #endif /* ((USB_CFG_DTC==USB_CFG_ENABLE)||(USB_CFG_DMA==USB_CFG_ENABLE)) */ |
| #endif /* DMA_DTC_NOT_ENABLED */ |
| |
| /* Do Aclr */ |
| |
| usb_cstd_do_aclrm(pipe); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_nrdy_endprocess |
| * Description : NRDY interrupt processing. (Forced termination of |
| * : data transmission and reception of specified pipe.) |
| * Arguments : uint16_t pipe : Pipe No |
| * Return value : none |
| * Note : none |
| ****************************************************************************/ |
| |
| void usb_hstd_nrdy_endprocess(uint16_t pipe) |
| { |
| uint16_t buffer; |
| |
| /* Host Function */ |
| |
| hw_usb_write_pipesel(pipe); /* Select the pipe */ |
| buffer = usb_cstd_get_pid(pipe); /* Read the PID of selected pipe */ |
| usb_cstd_set_nak(pipe); /* Set PIPE to NAK. Stop sending tokens. */ |
| |
| /* STALL ? */ |
| |
| if (RX65N_USB_DCPCTR_PIDSTALL == (buffer & RX65N_USB_DCPCTR_PIDSTALL)) |
| { |
| usb_hstd_forced_termination(pipe, USB_DATA_STALL); |
| } |
| else |
| { |
| /* Wait for About 60ns */ |
| |
| buffer = hw_usb_read_syssts(); |
| |
| if (nrdy_retries[pipe] >= 1) |
| { |
| /* Data Device Ignore X 3 call back */ |
| |
| /* End of data transfer */ |
| |
| usb_hstd_forced_termination(pipe, USB_DATA_TMO); |
| |
| g_rx65n_edlist[pipe].xfrinfo->tdstatus = TD_CC_DEVNOTRESPONDING; |
| |
| /* Release the semaphore for this pipe */ |
| |
| nxsem_post(&g_rx65n_edlist[pipe].wdhsem); |
| } |
| else |
| { |
| nrdy_retries[pipe]++; /* Increment and note error count */ |
| |
| /* 5ms wait */ |
| |
| up_mdelay(1); |
| |
| /* PIPEx Data Retry */ |
| |
| usb_hstd_data_end(pipe, USB_DATA_TMO); |
| hw_usb_set_bclr(USB_CUSE); /* Clear Buffer on CPU side */ |
| g_rx65n_edlist[pipe].xfrinfo->tdstatus = TD_CC_DEVNOTRESPONDING; |
| |
| /* 5ms wait */ |
| |
| usb_cstd_pipe_init(pipe); |
| |
| /* Release the semaphore for this pipe */ |
| |
| nxsem_post(&g_rx65n_edlist[pipe].wdhsem); |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_bus_int_disable |
| * Description : Disable USB Bus Interrupts OVRCR, ATTCH, DTCH, and BCHG. |
| * Arguments : none |
| * Return : none |
| ****************************************************************************/ |
| |
| void usb_hstd_bus_int_disable(void) |
| { |
| /* ATTCH interrupt disable */ |
| |
| usb_hstd_attch_disable(); |
| |
| /* DTCH interrupt disable */ |
| |
| usb_hstd_dtch_disable(); |
| |
| /* BCHG interrupt disable */ |
| |
| usb_hstd_bchg_disable(); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_attach |
| * Description : Set USB registers as required when USB device is |
| * : attached, and notify MGR (manager) task that attach |
| * : event occurred. |
| * Arguments : uint16_t result : Result. |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_attach(uint16_t result) |
| { |
| /* DTCH interrupt enable */ |
| |
| usb_hstd_dtch_enable(); |
| |
| /* Interrupt Enable */ |
| |
| usb_hstd_berne_enable(); |
| |
| usb_hstd_bus_reset(); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_detach |
| * Description : Set USB register as required when USB device is detached |
| * : and notify MGR (manager) task that detach occurred. |
| * Arguments : none |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_detach(void) |
| { |
| /* DVSTCTR clear */ |
| |
| hw_usb_clear_dvstctr(RX65N_USB_DVSTCTR0_RWUPE | |
| RX65N_USB_DVSTCTR0_USBRST | |
| RX65N_USB_DVSTCTR0_RESUME | |
| RX65N_USB_DVSTCTR0_UACT); |
| |
| /* ATTCH interrupt enable */ |
| |
| usb_hstd_attch_enable(); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_cstd_get_buf_size |
| * Description : Return buffer size, or max packet size, of specified |
| * : pipe. |
| * Arguments : uint16_t pipe : Pipe number. |
| * Return value : uint16_t : FIFO buffer size or max packet size. |
| ****************************************************************************/ |
| |
| static uint16_t usb_cstd_get_buf_size(uint16_t pipe) |
| { |
| uint16_t size = 0; |
| uint16_t buffer; |
| |
| if (USB_PIPE0 == pipe) |
| { |
| /* Not continuation transmit */ |
| |
| buffer = hw_usb_read_dcpmaxp(); |
| |
| /* Max Packet Size */ |
| |
| size = buffer & RX65N_USB_DCPMAXP_MXPS_MASK; |
| } |
| else |
| { |
| /* Pipe select */ |
| |
| hw_usb_write_pipesel(pipe); |
| |
| /* Read the maximum packet size of selected pipe */ |
| |
| buffer = hw_usb_read_pipemaxp(); |
| |
| /* Max Packet Size */ |
| |
| size = buffer & RX65N_USB_PIPEMAXP_MXPSMASK; |
| } |
| |
| return size; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_cstd_pipe_init |
| * Description : Initialization of registers associated with specified |
| * : pipe. |
| * Arguments : uint16_t pipe : Pipe Number |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void usb_cstd_pipe_init(uint16_t pipe) |
| { |
| /* Interrupt Disable */ |
| |
| /* Ready Int Disable */ |
| |
| hw_usb_clear_brdyenb(pipe); |
| |
| /* NotReady Int Disable */ |
| |
| hw_usb_clear_nrdyenb(pipe); |
| |
| /* Empty/SizeErr Int Disable */ |
| |
| hw_usb_clear_bempenb(pipe); |
| |
| /* PID=NAK & clear STALL */ |
| |
| usb_cstd_clr_stall(pipe); |
| |
| /* PIPE Configuration */ |
| |
| hw_usb_write_pipesel(pipe); |
| |
| rx65n_usbhost_putreg(pipe, RX65N_USB_PIPESEL); |
| rx65n_usbhost_putreg(g_usb_pipe_table[pipe].pipe_cfg, |
| RX65N_USB_PIPECFG); |
| rx65n_usbhost_putreg(g_usb_pipe_table[pipe].pipe_maxp, |
| RX65N_USB_PIPEMAXP); |
| rx65n_usbhost_putreg(g_usb_pipe_table[pipe].pipe_peri, |
| RX65N_USB_PIPEPERI); |
| |
| /* FIFO buffer DATA-PID initialized */ |
| |
| hw_usb_write_pipesel(USB_PIPE0); |
| |
| /* SQCLR */ |
| |
| hw_usb_set_sqclr(pipe); |
| |
| /* ACLRM */ |
| |
| usb_cstd_do_aclrm(pipe); |
| |
| /* Interrupt status clear */ |
| |
| /* Ready Int Clear */ |
| |
| hw_usb_clear_sts_brdy(pipe); |
| |
| /* NotReady Int Clear */ |
| |
| hw_usb_clear_status_nrdy(pipe); |
| |
| /* Empty/SizeErr Int Clear */ |
| |
| hw_usb_clear_status_bemp(pipe); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_cstd_clr_pipe_cnfg |
| * Description : Clear specified pipe configuration register. |
| * Arguments : uint16_t pipe_no : pipe number |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void usb_cstd_clr_pipe_cnfg(uint16_t pipe_no) |
| { |
| /* PID=NAK & clear STALL */ |
| |
| usb_cstd_clr_stall(pipe_no); |
| |
| /* Interrupt disable */ |
| |
| /* Ready Int Disable */ |
| |
| hw_usb_clear_brdyenb(pipe_no); |
| |
| /* NotReady Int Disable */ |
| |
| hw_usb_clear_nrdyenb(pipe_no); |
| |
| /* Empty/SizeErr Int Disable */ |
| |
| hw_usb_clear_bempenb(pipe_no); |
| |
| /* PIPE Configuration */ |
| |
| usb_cstd_chg_curpipe(USB_PIPE0, USB_CUSE, USB_FALSE); |
| hw_usb_write_pipesel(pipe_no); |
| hw_usb_write_pipecfg(0); |
| |
| hw_usb_write_pipemaxp(0); |
| hw_usb_write_pipeperi(0); |
| hw_usb_write_pipesel(0); |
| |
| /* FIFO buffer DATA-PID initialized */ |
| |
| /* SQCLR */ |
| |
| hw_usb_set_sqclr(pipe_no); |
| |
| /* ACLRM */ |
| |
| usb_cstd_do_aclrm(pipe_no); |
| |
| usb_cstd_clr_transaction_counter(pipe_no); |
| |
| /* Interrupt status clear */ |
| |
| /* Ready Int Clear */ |
| |
| hw_usb_clear_sts_brdy(pipe_no); |
| |
| /* NotReady Int Clear */ |
| |
| hw_usb_clear_status_nrdy(pipe_no); |
| |
| /* Empty/SizeErr Int Clear */ |
| |
| hw_usb_clear_status_bemp(pipe_no); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_cstd_set_nak |
| * Description : Set up to NAK the specified pipe. |
| * Arguments : uint16_t pipe : Pipe Number |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void usb_cstd_set_nak(uint16_t pipe) |
| { |
| uint16_t buf; |
| uint16_t n; |
| |
| /* Set NAK */ |
| |
| hw_usb_clear_pid(pipe, USB_PID_BUF); |
| |
| /* The state of PBUSY continues while transmitting the packet |
| * when it is a detach. 1ms comes off when leaving because the |
| * packet duration might not exceed 1ms. |
| */ |
| |
| /* Whether it is PBUSY release or 1ms passage can be judged. */ |
| |
| /* WAIT_LOOP */ |
| |
| for (n = 0; n < 0xffffu; ++n) |
| { |
| /* PIPE control reg read */ |
| |
| buf = hw_usb_read_pipectr(pipe); |
| if (0 == (buf & RX65N_USB_DCPCTR_PID_MASK)) |
| { |
| n = 0xfffeu; |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_cstd_is_set_frdy |
| * Description : Changes the specified FIFO port by the specified pipe. |
| * Arguments : uint16_t pipe : Pipe Number |
| * : uint16_t fifosel : FIFO select |
| * : uint16_t isel : ISEL bit status |
| * Return value : FRDY status |
| ****************************************************************************/ |
| |
| static uint16_t usb_cstd_is_set_frdy(uint16_t pipe, uint16_t fifosel, |
| uint16_t isel) |
| { |
| uint16_t buffer; |
| uint16_t i; |
| |
| /* Changes the FIFO port by the pipe. */ |
| |
| usb_cstd_chg_curpipe(pipe, fifosel, isel); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < 4; i++) |
| { |
| buffer = hw_usb_read_fifoctr(fifosel); |
| |
| if (RX65N_USB_FIFOCTR_FRDY == (buffer & |
| RX65N_USB_FIFOCTR_FRDY)) |
| { |
| return buffer; |
| } |
| |
| buffer = hw_usb_read_syscfg(); |
| buffer = hw_usb_read_syssts(); |
| nxsig_usleep(1); |
| } |
| |
| return RX65N_USB_FIFO_ERROR; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_cstd_chg_curpipe |
| * Description : Switch FIFO and pipe number. |
| * Arguments : uint16_t pipe : Pipe number. |
| * : uint16_t fifosel : FIFO selected (CPU, D0, D1..) |
| * : uint16_t isel : CFIFO Port Access Direction. |
| * : (Pipe1 to 9:Set to 0) |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_cstd_chg_curpipe(uint16_t pipe, uint16_t fifosel, uint16_t isel) |
| { |
| uint16_t buffer; |
| |
| /* Select FIFO */ |
| |
| switch (fifosel) |
| { |
| /* CFIFO use */ |
| |
| case RX65N_USB_USING_CFIFO: |
| |
| /* ISEL=1, CURPIPE=0 */ |
| |
| hw_usb_rmw_fifosel(USB_CUSE, ((USB_RCNT | isel) | pipe), |
| ((USB_RCNT | USB_ISEL) | USB_CURPIPE)); |
| |
| /* WAIT_LOOP */ |
| |
| do |
| { |
| buffer = hw_usb_read_fifosel(USB_CUSE); |
| } |
| |
| while ((buffer & (USB_ISEL | USB_CURPIPE)) != (isel | pipe)); |
| break; |
| |
| #ifdef DTC_DMA_ENABLED |
| #if ((USB_CFG_DTC == USB_CFG_ENABLE) || (USB_CFG_DMA == USB_CFG_ENABLE)) |
| |
| /* D0FIFO use */ |
| |
| case USB_D0USE: |
| |
| /* D1FIFO use */ |
| |
| case USB_D1USE: |
| |
| /* DxFIFO pipe select */ |
| |
| hw_usb_set_curpipe(fifosel, pipe); |
| |
| /* WAIT_LOOP */ |
| |
| do |
| { |
| buffer = hw_usb_read_fifosel(fifosel); |
| } |
| while ((buffer & USB_CURPIPE) != pipe); |
| break; |
| |
| #endif /* ((USB_CFG_DTC==USB_CFG_ENABLE||(USB_CFG_DMA==USB_CFG_ENABLE))*/ |
| #endif /* DTC_DMA_ENABLED */ |
| |
| default: |
| break; |
| } |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_cstd_set_transaction_counter |
| * Description : Set specified Pipe Transaction Counter Register. |
| * Arguments : uint16_t trnreg : Pipe number |
| * : uint16_t trncnt : Transaction counter |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void usb_cstd_set_transaction_counter(uint16_t trnreg, |
| uint16_t trncnt) |
| { |
| hw_usb_set_trclr(trnreg); |
| hw_usb_write_pipetrn(trnreg, trncnt); |
| hw_usb_set_trenb(trnreg); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_cstd_clr_transaction_counter |
| * Description : Clear specified Pipe Transaction Counter Register. |
| * Arguments : uint16_t trnreg : Pipe Number |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void usb_cstd_clr_transaction_counter(uint16_t trnreg) |
| { |
| hw_usb_clear_trenb(trnreg); |
| hw_usb_set_trclr(trnreg); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_cstd_nrdy_enable |
| * Description : Enable NRDY interrupt of the specified pipe. |
| * Arguments : uint16_t pipe : Pipe number. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void usb_cstd_nrdy_enable(uint16_t pipe) |
| { |
| if (USB_MAX_PIPE_NO < pipe) |
| { |
| return; /* Error */ |
| } |
| |
| /* Enable NRDY */ |
| |
| hw_usb_set_nrdyenb(pipe); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_cstd_get_pid |
| * Description : Fetch specified pipe's PID. |
| * Arguments : uint16_t pipe : Pipe number. |
| * Return value : uint16_t PID-bit status |
| ****************************************************************************/ |
| |
| static uint16_t usb_cstd_get_pid(uint16_t pipe) |
| { |
| uint16_t buf; |
| |
| if (USB_MAX_PIPE_NO < pipe) |
| { |
| return USB_NULL; /* Error */ |
| } |
| |
| /* PIPE control reg read */ |
| |
| buf = hw_usb_read_pipectr(pipe); |
| return buf & RX65N_USB_DCPCTR_PID_MASK; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_cstd_get_maxpacket_size |
| * Description : Fetch MaxPacketSize of the specified pipe. |
| * Arguments : uint16_t pipe : Pipe number. |
| * Return value : uint16_t MaxPacketSize |
| ****************************************************************************/ |
| |
| static uint16_t usb_cstd_get_maxpacket_size(uint16_t pipe) |
| { |
| uint16_t size; |
| uint16_t buffer; |
| |
| if (USB_MAX_PIPE_NO < pipe) |
| { |
| return USB_NULL; /* Error */ |
| } |
| |
| if (USB_PIPE0 == pipe) |
| { |
| buffer = hw_usb_read_dcpmaxp(); |
| } |
| else |
| { |
| /* Pipe select */ |
| |
| hw_usb_write_pipesel(pipe); |
| buffer = hw_usb_read_pipemaxp(); |
| } |
| |
| /* Max Packet Size */ |
| |
| size = (buffer & RX65N_USB_DCPMAXP_MXPS_MASK); |
| |
| return size; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_cstd_get_pipe_dir |
| * Description : Get PIPE DIR |
| * Arguments : uint16_t pipe : Pipe number. |
| * Return value : uint16_t pipe direction. |
| ****************************************************************************/ |
| |
| static uint16_t usb_cstd_get_pipe_dir(uint16_t pipe) |
| { |
| uint16_t buffer; |
| |
| if (USB_MAX_PIPE_NO < pipe) |
| { |
| return USB_NULL; /* Error */ |
| } |
| |
| /* Pipe select */ |
| |
| hw_usb_write_pipesel(pipe); |
| |
| /* Read Pipe direction */ |
| |
| buffer = hw_usb_read_pipecfg(); |
| return buffer & RX65N_USB_PIPECFG_DIR; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_cstd_do_aclrm |
| * Description : Set the ACLRM-bit (Auto Buffer Clear Mode) of the |
| * : specified pipe. |
| * Arguments : uint16_t pipe : Pipe number. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void usb_cstd_do_aclrm(uint16_t pipe) |
| { |
| if (USB_MAX_PIPE_NO < pipe) |
| { |
| return; /* Error */ |
| } |
| |
| /* Control ACLRM */ |
| |
| hw_usb_set_aclrm(pipe); |
| hw_usb_clear_aclrm(pipe); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_cstd_set_buf |
| * Description : Set PID (packet ID) of the specified pipe to BUF. |
| * Arguments : uint16_t pipe : Pipe number. |
| * Return value : none |
| ****************************************************************************/ |
| |
| static void usb_cstd_set_buf(uint16_t pipe) |
| { |
| if (USB_MAX_PIPE_NO < pipe) |
| { |
| return; /* Error */ |
| } |
| |
| /* PIPE control reg set */ |
| |
| hw_usb_set_pid(pipe, RX65N_USB_DCPCTR_PIDBUF); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_cstd_clr_stall |
| * Description : Set up to NAK the specified pipe, and clear the |
| * : STALL-bit set to the PID of the specified pipe. |
| * Arguments : usb_utr_t *ptr : Pointer to usb_utr_t structure. |
| * : uint16_t pipe : Pipe number. |
| * Return value : none |
| * Note : PID is set to NAK. |
| ****************************************************************************/ |
| |
| static void usb_cstd_clr_stall(uint16_t pipe) |
| { |
| if (USB_MAX_PIPE_NO < pipe) |
| { |
| return; /* Error */ |
| } |
| |
| /* Set NAK */ |
| |
| usb_cstd_set_nak(pipe); |
| |
| /* Clear STALL */ |
| |
| hw_usb_clear_pid(pipe, RX65N_USB_DCPCTR_PIDSTALL); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_ctrl_write_start |
| * Description : Start data stage of Control Write transfer. |
| * Arguments : uint32_t Bsize : Data Size |
| * : uint8_t *Table : Data Table Address |
| * Return : uint16_t : USB_WRITEEND / USB_WRITING |
| * : : USB_WRITESHRT / USB_FIFOERROR |
| ****************************************************************************/ |
| |
| uint16_t usb_hstd_ctrl_write_start(uint8_t *buf_add, size_t buf_size) |
| { |
| uint16_t end_flag; |
| |
| /* PID=NAK & clear STALL */ |
| |
| usb_cstd_clr_stall(USB_PIPE0); |
| |
| /* DCP Configuration Register (0x5c) */ |
| |
| hw_usb_write_dcpcfg((USB_CNTMDFIELD | USB_DIRFIELD)); |
| |
| hw_usb_set_sqset(USB_PIPE0); /* SQSET=1, PID=NAK */ |
| |
| hw_usb_clear_status_bemp(USB_PIPE0); |
| |
| end_flag = usb_hstd_write_data_control_pipe(buf_add, buf_size); |
| |
| switch (end_flag) |
| { |
| /* End of data write */ |
| |
| case USB_WRITESHRT: |
| |
| /* Next stage is Control write status stage */ |
| |
| /* Enable Empty Interrupt */ |
| |
| hw_usb_set_bempenb(USB_PIPE0); |
| |
| /* Enable Not Ready Interrupt */ |
| |
| usb_cstd_nrdy_enable(USB_PIPE0); |
| |
| /* Set BUF */ |
| |
| usb_cstd_set_buf(USB_PIPE0); |
| break; |
| |
| /* End of data write (not null) */ |
| |
| case USB_WRITEEND: |
| |
| /* continue */ |
| |
| /* Continue of data write */ |
| |
| case USB_WRITING: |
| |
| /* Enable Empty Interrupt */ |
| |
| hw_usb_set_bempenb(USB_PIPE0); |
| |
| /* Enable Not Ready Interrupt */ |
| |
| usb_cstd_nrdy_enable(USB_PIPE0); |
| |
| /* Set BUF */ |
| |
| usb_cstd_set_buf(USB_PIPE0); |
| break; |
| |
| /* FIFO access error */ |
| |
| case USB_FIFOERROR: |
| break; |
| |
| default: |
| break; |
| } |
| |
| /* End or Err or Continue */ |
| |
| return end_flag; |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_ctrl_read_start |
| * Description : Start data stage of Control Read transfer. |
| * Arguments : none |
| * Return : none |
| ****************************************************************************/ |
| |
| void usb_hstd_ctrl_read_start(void) |
| { |
| /* PID=NAK & clear STALL */ |
| |
| usb_cstd_clr_stall(USB_PIPE0); |
| |
| /* DCP Configuration Register (0x5c) */ |
| |
| hw_usb_write_dcpcfg(RX65N_USB_PIPECFG_SHTNAK); |
| |
| /* SQSET=1, PID=NAK */ |
| |
| hw_usb_hwrite_dcpctr(RX65N_USB_DCPCTR_SQSET); |
| |
| /* Interrupt enable */ |
| |
| /* Enable Ready Interrupt */ |
| |
| hw_usb_set_brdyenb(USB_PIPE0); |
| |
| /* Enable Not Ready Interrupt */ |
| |
| usb_cstd_nrdy_enable(USB_PIPE0); |
| usb_cstd_set_buf(USB_PIPE0); /* Set BUF */ |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_host_read_pipe_start |
| * Description : Start data stage of data Read transfer. |
| * Arguments : uint32_t Bsize : Data Size |
| * : uint8_t *Table : Data Table Address |
| * Return : none |
| ****************************************************************************/ |
| |
| void usb_host_read_pipe_start(uint16_t pipe) |
| { |
| uint16_t config_reg; |
| uint16_t ctrl_reg; |
| |
| /* PID=NAK & clear STALL */ |
| |
| usb_cstd_clr_stall(pipe); |
| |
| config_reg = hw_usb_read_pipecfg(); |
| |
| /* Pipe disabled at the end of transfer i.e. it is set to NAK */ |
| |
| config_reg = config_reg | RX65N_USB_PIPECFG_SHTNAK; |
| |
| /* Set the direction as read */ |
| |
| config_reg = config_reg & ~(RX65N_USB_PIPECFG_DIR); |
| hw_usb_write_pipecfg(config_reg); |
| |
| ctrl_reg = hw_usb_read_pipectr (pipe); |
| ctrl_reg = ctrl_reg | RX65N_USB_PIPECTR_SQSET; |
| hw_usb_write_pipectr(pipe, ctrl_reg); |
| |
| /* usb_hstd_do_sqtgl((uint16_t) USB_PIPE0, 0); */ |
| |
| /* Interrupt enable */ |
| |
| /* Enable Ready Interrupt */ |
| |
| hw_usb_set_brdyenb(pipe); |
| |
| /* Enable Not Ready Interrupt */ |
| |
| usb_cstd_nrdy_enable(pipe); |
| |
| /* Set BUF */ |
| |
| usb_cstd_set_buf(pipe); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_status_start |
| * Description : Start status stage of Control Command. |
| * Arguments : none |
| ****************************************************************************/ |
| |
| void usb_hstd_status_start(void) |
| { |
| /* Interrupt Disable */ |
| |
| /* BEMP0 Disable */ |
| |
| hw_usb_clear_bempenb(USB_PIPE0); |
| |
| /* BRDY0 Disable */ |
| |
| hw_usb_clear_brdyenb(USB_PIPE0); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_ctrl_end |
| * Description : Call the user registered callback function that notifies |
| * : completion of a control transfer. |
| * Arguments : uint16_t status : Transfer status |
| * Return : none |
| ****************************************************************************/ |
| |
| void usb_hstd_ctrl_end(uint16_t status) |
| { |
| /* Interrupt Disable */ |
| |
| hw_usb_clear_bempenb(USB_PIPE0); /* BEMP0 Disable */ |
| hw_usb_clear_brdyenb(USB_PIPE0); /* BRDY0 Disable */ |
| hw_usb_clear_nrdyenb(USB_PIPE0); /* NRDY0 Disable */ |
| |
| usb_cstd_clr_stall(USB_PIPE0); /* PID=NAK & clear STALL */ |
| hw_usb_set_mbw(USB_CUSE, USB0_CFIFO_MBW); |
| |
| /* SUREQ=1, SQCLR=1, PID=NAK */ |
| |
| hw_usb_hwrite_dcpctr((USB_SUREQCLR | USB_SQCLR); |
| |
| /* CFIFO buffer clear */ |
| |
| usb_cstd_chg_curpipe(USB_PIPE0, USB_CUSE, USB_FALSE); |
| hw_usb_set_bclr(USB_CUSE); /* Clear BVAL */ |
| usb_cstd_chg_curpipe((USB_PIPE0, USB_CUSE, USB_ISEL); |
| hw_usb_set_bclr(USB_CUSE); /* Clear BVAL */ |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_brdy_pipe |
| * Description : BRDY Interrupt |
| * Arguments : uint16_t bitsts : BRDYSTS Reg & BRDYENB Reg |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_brdy_pipe(void) |
| { |
| uint16_t bitsts; |
| |
| bitsts = rx65n_usbhost_getreg(RX65N_USB_BRDYSTS); |
| |
| /* When operating by the host function, usb_hstd_brdy_pipe() is executed |
| * without fail because only one BRDY message is issued even when the |
| * demand of PIPE0 and PIPEx has been generated at the same time. |
| */ |
| |
| if (USB_BRDY0 == (bitsts & USB_BRDY0)) |
| { |
| hw_usb_clear_sts_brdy(USB_PIPE0); |
| |
| /* Branch by the Control transfer stage management */ |
| |
| if (EDCTRL->xfrinfo->tdxfercond == USB_STATUSRD) |
| { |
| /* This interrupt occurred due to setup packet status stage of |
| * writing NULL packet. |
| */ |
| |
| /* Call this to end the setup packet */ |
| |
| /* Control Read/Write End */ |
| |
| usb_hstd_ctrl_end(USB_CTRL_END); |
| |
| nxsem_post(&EDCTRL->wdhsem); |
| return; /* Nothing else to do here... as of now... */ |
| } |
| |
| switch (EDCTRL->xfrinfo->tdxfercond) |
| { |
| /* Data stage of Control read transfer */ |
| |
| case USB_DATARD: |
| EDCTRL->xfrinfo->tdxfercond = usb_hstd_read_data_control_pipe (); |
| |
| switch (EDCTRL->xfrinfo->tdxfercond) |
| { |
| /* End of data read */ |
| |
| case USB_READEND: |
| |
| /* continue */ |
| |
| /* End of data read */ |
| |
| case USB_READSHRT: |
| |
| usb_hstd_status_start(); |
| break; |
| |
| case USB_READING: /* Continue of data read */ |
| |
| /* Still data is there - so set the condition for |
| * reading in next interrupt... |
| */ |
| |
| EDCTRL->xfrinfo->tdxfercond = USB_DATARD; |
| break; |
| |
| case USB_READOVER: /* FIFO access error */ |
| |
| /* Control Read/Write End */ |
| |
| usb_hstd_ctrl_end(USB_DATA_OVR); |
| break; |
| |
| case USB_FIFOERROR: /* FIFO access error */ |
| |
| /* Control Read/Write End */ |
| |
| usb_hstd_ctrl_end(USB_DATA_ERR); |
| syslog(LOG_INFO, "ERROR"); |
| break; |
| |
| default: |
| break; |
| } |
| break; |
| |
| /* Data stage of Control read transfer */ |
| |
| case USB_DATARDCNT: |
| |
| switch (usb_hstd_read_data_control_pipe()) |
| { |
| case USB_READEND: /* End of data read */ |
| |
| /* Control Read/Write End */ |
| |
| usb_hstd_ctrl_end(USB_CTRL_READING); |
| break; |
| |
| case USB_READSHRT: /* End of data read */ |
| |
| /* Control Read/Write Status */ |
| |
| usb_hstd_status_start(); |
| break; |
| |
| case USB_READING: /* Continue of data read */ |
| |
| /* Still data is there - so set the condition for |
| * reading in next interrupt... |
| */ |
| |
| EDCTRL->xfrinfo->tdxfercond = USB_DATARDCNT; |
| break; |
| |
| case USB_READOVER: /* FIFO access error */ |
| |
| /* Control Read/Write End */ |
| |
| usb_hstd_ctrl_end(USB_DATA_OVR); |
| break; |
| |
| case USB_FIFOERROR: /* FIFO access error */ |
| |
| /* Control Read/Write End */ |
| |
| usb_hstd_ctrl_end(USB_DATA_ERR); |
| syslog(LOG_INFO, "ERROR"); |
| break; |
| |
| default: |
| break; |
| } |
| break; |
| |
| /* Status stage Control write (NoData control) transfer */ |
| |
| case USB_STATUSWR: |
| |
| /* Control Read/Write End */ |
| |
| usb_hstd_ctrl_end(USB_CTRL_END); |
| break; |
| |
| default: |
| break; |
| } |
| |
| if ((EDCTRL->xfrinfo->tdxfercond == USB_READEND) || |
| (EDCTRL->xfrinfo->tdxfercond == USB_READSHRT) || |
| (EDCTRL->xfrinfo->tdxfercond == USB_READOVER)) |
| { |
| nxsem_post(&EDCTRL->wdhsem); |
| } |
| |
| hw_usb_clear_sts_brdy(USB_PIPE0); /* This was missing? */ |
| } |
| |
| /* BRDY interrupt */ |
| |
| usb_hstd_brdy_pipe_process(bitsts); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_nrdy_pipe |
| * Description : NRDY Interrupt |
| * Arguments : usb_utr_t *ptr : Pointer to usb_utr_t structure. |
| * : uint16_t bitsts : NRDYSTS Reg & NRDYENB Reg |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_nrdy_pipe(void) |
| { |
| uint16_t buffer; |
| uint16_t bitsts; |
| |
| bitsts = rx65n_usbhost_getreg(RX65N_USB_NRDYSTS); /* ptr->status; */ |
| |
| /* When operating by the host function, usb_hstd_nrdy_pipe() |
| * is executed without fail because |
| * only one NRDY message is issued even when the demand of |
| * PIPE0 and PIPEx has been generated at the same time. |
| */ |
| |
| if (USB_NRDY0 == (bitsts & USB_NRDY0)) |
| { |
| hw_usb_clear_status_nrdy(USB_PIPE0); |
| |
| /* Get Pipe PID from pipe number */ |
| |
| buffer = usb_cstd_get_pid(USB_PIPE0); |
| |
| /* STALL ? */ |
| |
| if (RX65N_USB_DCPCTR_PIDSTALL == (buffer & |
| RX65N_USB_DCPCTR_PIDSTALL)) |
| { |
| /* PIPE0 STALL call back */ |
| |
| usb_hstd_ctrl_end(USB_DATA_STALL); |
| } |
| else |
| { |
| /* Control Data Stage Device Ignore X 3 call back */ |
| |
| usb_hstd_ctrl_end(USB_DATA_ERR); |
| } |
| } |
| |
| /* Nrdy Pipe interrupt */ |
| |
| usb_hstd_nrdy_pipe_process(bitsts); |
| } |
| |
| /**************************************************************************** |
| * Function Name : usb_hstd_bemp_pipe |
| * Description : BEMP interrupt |
| * Arguments : uint16_t bitsts : BEMPSTS Reg & BEMPENB Reg |
| * Return value : none |
| ****************************************************************************/ |
| |
| void usb_hstd_bemp_pipe(void) |
| { |
| uint16_t buffer; |
| uint16_t bitsts; |
| |
| bitsts = rx65n_usbhost_getreg(RX65N_USB_BEMPSTS); /* ptr->status; */ |
| |
| /* When operating by the host function, usb_hstd_bemp_pipe() |
| * is executed without fail because |
| * only one BEMP message is issued even when the demand of PIPE0 |
| * and PIPEx has been generated at the same time. |
| */ |
| |
| if (USB_BEMP0 == (bitsts & USB_BEMP0)) |
| { |
| hw_usb_clear_status_bemp(USB_PIPE0); |
| if (EDCTRL->xfrinfo->tdxfercond == USB_STATUSWR) |
| { |
| /* This interrupt occurred due to setup packet status |
| * stage of writing NULL packet. |
| */ |
| |
| /* BEMP0 Disable */ |
| |
| hw_usb_clear_bempenb(USB_PIPE0); |
| |
| /* BRDY0 Disable */ |
| |
| hw_usb_clear_brdyenb(USB_PIPE0); |
| |
| /* Call this to end the setup packet */ |
| |
| /* Control Read/Write End */ |
| |
| usb_hstd_ctrl_end(USB_CTRL_END); |
| |
| nxsem_post(&EDCTRL->wdhsem); |
| return; /* As of now, Nothing else to do here... */ |
| } |
| |
| /* Get Pipe PID from pipe number */ |
| |
| buffer = usb_cstd_get_pid(USB_PIPE0); |
| |
| /* MAX packet size error ? */ |
| |
| if (RX65N_USB_DCPCTR_PIDSTALL == (buffer & RX65N_USB_DCPCTR_PIDSTALL)) |
| { |
| /* PIPE0 STALL call back */ |
| |
| usb_hstd_ctrl_end(USB_DATA_STALL); |
| } |
| else |
| { |
| /* Branch by the Control transfer stage management */ |
| |
| switch (bitsts) |
| { |
| /* Continuas of data stage (Control write) */ |
| |
| case USB_DATAWR: |
| |
| /* We should not get into this... */ |
| |
| /* Buffer to CFIFO data write */ |
| |
| switch (usb_hstd_write_data_control_pipe(0, 0)) |
| { |
| /* End of data write */ |
| |
| case USB_WRITESHRT: |
| |
| hw_usb_set_bempenb(USB_PIPE0); |
| |
| /* Enable Not Ready Interrupt */ |
| |
| usb_cstd_nrdy_enable(USB_PIPE0); |
| break; |
| |
| /* End of data write (not null) */ |
| |
| case USB_WRITEEND: |
| |
| /* continue */ |
| |
| /* Continue of data write */ |
| |
| case USB_WRITING: |
| |
| /* Enable Empty Interrupt */ |
| |
| hw_usb_set_bempenb(USB_PIPE0); |
| |
| /* Enable Not Ready Interrupt */ |
| |
| usb_cstd_nrdy_enable(USB_PIPE0); |
| break; |
| |
| /* FIFO access error */ |
| |
| case USB_FIFOERROR: |
| |
| /* Control Read/Write End */ |
| |
| usb_hstd_ctrl_end(USB_DATA_ERR); |
| break; |
| |
| default: |
| break; |
| } |
| break; |
| |
| /* Next stage to Control write data */ |
| |
| case USB_DATAWRCNT: |
| |
| /* Buffer to CFIFO data write */ |
| |
| /* We should not get here... */ |
| |
| switch (usb_hstd_write_data_control_pipe(0, 0)) |
| { |
| /* End of data write */ |
| |
| case USB_WRITESHRT: |
| |
| hw_usb_set_bempenb(USB_PIPE0); |
| |
| /* Enable Not Ready Interrupt */ |
| |
| usb_cstd_nrdy_enable(USB_PIPE0); |
| break; |
| |
| /* End of data write (not null) */ |
| |
| case USB_WRITEEND: |
| |
| /* Control Read/Write End */ |
| |
| usb_hstd_ctrl_end(USB_CTRL_WRITING); |
| break; |
| |
| /* Continue of data write */ |
| |
| case USB_WRITING: |
| |
| /* Enable Empty Interrupt */ |
| |
| hw_usb_set_bempenb(USB_PIPE0); |
| |
| /* Enable Not Ready Interrupt */ |
| |
| usb_cstd_nrdy_enable(USB_PIPE0); |
| break; |
| |
| /* FIFO access error */ |
| |
| case USB_FIFOERROR: |
| |
| /* Control Read/Write End */ |
| |
| usb_hstd_ctrl_end(USB_DATA_ERR); |
| break; |
| |
| default: |
| break; |
| } |
| break; |
| |
| case USB_STATUSWR: /* End of data stage (Control write) */ |
| usb_hstd_status_start(); |
| break; |
| |
| /* Status stage of Control read transfer */ |
| |
| case USB_STATUSRD: |
| |
| /* Control Read/Write End */ |
| |
| usb_hstd_ctrl_end(USB_CTRL_END); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| } |
| |
| usb_hstd_bemp_pipe_process(bitsts); /* BEMP interrupt */ |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_printreg |
| * |
| * Description: |
| * Print the contents of an RX65N register operation |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_RX65N_USBHOST_REGDEBUG |
| static void rx65n_usbhost_printreg(uint32_t addr, uint32_t val, bool iswrite) |
| { |
| uinfo("%08x%s%08x\n", addr, iswrite ? "<-" : "->", val); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_checkreg |
| * |
| * Description: |
| * Get the contents of an RX65N register |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_RX65N_USBHOST_REGDEBUG |
| static void rx65n_usbhost_checkreg(uint32_t addr, uint32_t val, bool iswrite) |
| { |
| static uint32_t prevaddr = 0; |
| static uint32_t preval = 0; |
| static uint32_t count = 0; |
| static bool prevwrite = false; |
| |
| /* Is this the same value that we read from/wrote to the same register |
| * last time? Are we polling the register? If so, suppress the output. |
| */ |
| |
| if (addr == prevaddr && val == preval && prevwrite == iswrite) |
| { |
| /* Yes.. Just increment the count */ |
| |
| count++; |
| } |
| else |
| { |
| /* No this is a new address or value or operation. Were there any |
| * duplicate accesses before this one? |
| */ |
| |
| if (count > 0) |
| { |
| /* Yes.. Just one? */ |
| |
| if (count == 1) |
| { |
| /* Yes.. Just one */ |
| |
| rx65n_usbhost_printreg(prevaddr, preval, prevwrite); |
| } |
| else |
| { |
| /* No.. More than one. */ |
| |
| uinfo("[repeats %d more times]\n", count); |
| } |
| } |
| |
| /* Save the new address, value, count, and operation for next time */ |
| |
| prevaddr = addr; |
| preval = val; |
| count = 0; |
| prevwrite = iswrite; |
| |
| /* Show the new regisgter access */ |
| |
| rx65n_usbhost_printreg(addr, val, iswrite); |
| } |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_getreg |
| * |
| * Description: |
| * Get the contents of an RX65N register |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_RX65N_USBHOST_REGDEBUG |
| static uint16_t rx65n_usbhost_getreg(uint32_t addr) |
| { |
| /* Read the value from the register */ |
| |
| uint16_t val = getreg16(addr); |
| |
| /* Check if we need to print this value */ |
| |
| rx65n_usbhost_checkreg(addr, val, false); |
| return val; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_putreg |
| * |
| * Description: |
| * Set the contents of an RX65N register to a value |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_RX65N_USBHOST_REGDEBUG |
| static void rx65n_usbhost_putreg(uint16_t val, uint32_t addr) |
| { |
| /* Check if we need to print this value */ |
| |
| rx65n_usbhost_checkreg(addr, val, true); |
| |
| /* Write the value */ |
| |
| putreg16(val, addr); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_getle16 |
| * |
| * Description: |
| * Get a (possibly unaligned) 16-bit little endian value. |
| * |
| ****************************************************************************/ |
| |
| static inline uint16_t rx65n_usbhost_getle16(const uint8_t *val) |
| { |
| return val[1] << 8 | val[0]; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_edfree |
| * |
| * Description: |
| * Return an endpoint descriptor to the free list |
| * |
| ****************************************************************************/ |
| |
| static inline void rx65n_usbhost_edfree(struct rx65n_usbhost_ed_s *ed) |
| { |
| struct rx65n_usbhost_list_s *entry = (struct rx65n_usbhost_list_s *)ed; |
| |
| /* Put the ED back into the free list */ |
| |
| entry->flink = g_edfree; |
| g_edfree = entry; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_tdalloc |
| * |
| * Description: |
| * Allocate an transfer descriptor from the free list |
| * |
| * Assumptions: |
| * - Never called from an interrupt handler. |
| * - Protected from concurrent access to the TD pool by the interrupt |
| * handler |
| * - Protection from re-entrance must be assured by the caller |
| * |
| ****************************************************************************/ |
| |
| static struct rx65n_usbhost_gtd_s *rx65n_usbhost_tdalloc(uint8_t ep_num) |
| { |
| /* Currently each TD would associate with one EP. So the ep_numb is |
| * passed to tdalloc function and it would return the TD with this, |
| * there is no need to free this |
| */ |
| |
| memset(&(g_rx65n_tdlist[ep_num]), 0, sizeof(struct rx65n_usbhost_gtd_s)); |
| return &(g_rx65n_tdlist[ep_num]); |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_tdfree |
| * |
| * Description: |
| * Return an transfer descriptor to the free list |
| * |
| * Assumptions: |
| * Only called from the WDH interrupt handler (and during initialization). |
| * Interrupts are disabled in any case. |
| * |
| ****************************************************************************/ |
| |
| static void rx65n_usbhost_tdfree(struct rx65n_usbhost_gtd_s *td) |
| { |
| struct rx65n_usbhost_list_s *tdfree = (struct rx65n_usbhost_list_s *)td; |
| |
| /* This should not happen but just to be safe, don't free the common, pre- |
| * allocated tail TD. |
| */ |
| |
| if (tdfree != NULL && td != TDTAIL) |
| { |
| tdfree->flink = g_tdfree; |
| g_tdfree = tdfree; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_tballoc |
| * |
| * Description: |
| * Allocate an request/descriptor transfer buffer from the free list |
| * |
| * Assumptions: |
| * - Never called from an interrupt handler. |
| * - Protection from re-entrance must be assured by the caller |
| * |
| ****************************************************************************/ |
| |
| static uint8_t *rx65n_usbhost_tballoc(void) |
| { |
| uint8_t *ret = (uint8_t *)g_tbfree; |
| |
| if (ret) |
| { |
| g_tbfree = ((struct rx65n_usbhost_list_s *)ret)->flink; |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_tbfree |
| * |
| * Description: |
| * Return an request/descriptor transfer buffer to the free list |
| * |
| ****************************************************************************/ |
| |
| static void rx65n_usbhost_tbfree(uint8_t *buffer) |
| { |
| struct rx65n_usbhost_list_s *tbfree = |
| (struct rx65n_usbhost_list_s *)buffer; |
| |
| if (tbfree) |
| { |
| tbfree->flink = g_tbfree; |
| g_tbfree = tbfree; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhhost_allocio |
| * |
| * Description: |
| * Allocate an IO buffer from the free list |
| * |
| * Assumptions: |
| * - Never called from an interrupt handler. |
| * - Protection from re-entrance must be assured by the caller |
| * |
| ****************************************************************************/ |
| |
| #if RX65N_USBHOST_IOBUFFERS > 0 |
| static uint8_t *rx65n_usbhhost_allocio(void) |
| { |
| uint8_t *ret; |
| irqstate_t flags; |
| |
| flags = enter_critical_section(); |
| ret = (uint8_t *)g_iofree; |
| if (ret) |
| { |
| g_iofree = ((struct rx65n_usbhost_list_s *)ret)->flink; |
| } |
| |
| leave_critical_section(flags); |
| return ret; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhhost_freeio |
| * |
| * Description: |
| * Return an TD buffer to the free list |
| * |
| ****************************************************************************/ |
| |
| #if RX65N_USBHOST_IOBUFFERS > 0 |
| static void rx65n_usbhhost_freeio(uint8_t *buffer) |
| { |
| struct rx65n_usbhost_list_s *iofree; |
| irqstate_t flags; |
| |
| /* Could be called from the interrupt level */ |
| |
| flags = enter_critical_section(); |
| iofree = (struct rx65n_usbhost_list_s *)buffer; |
| iofree->flink = g_iofree; |
| g_iofree = iofree; |
| leave_critical_section(flags); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_alloc_xfrinfo |
| * |
| * Description: |
| * Allocate an asynchronous data structure from the free list |
| * |
| * Assumptions: |
| * - Never called from an interrupt handler. |
| * - Protection from re-entrance must be assured by the caller |
| * |
| ****************************************************************************/ |
| |
| static struct rx65n_usbhost_xfrinfo_s *rx65n_usbhost_alloc_xfrinfo(void) |
| { |
| struct rx65n_usbhost_xfrinfo_s *ret; |
| irqstate_t flags; |
| |
| flags = enter_critical_section(); |
| ret = (struct rx65n_usbhost_xfrinfo_s *)g_xfrfree; |
| |
| if (ret) |
| { |
| g_xfrfree = ((struct rx65n_usbhost_list_s *)ret)->flink; |
| } |
| |
| leave_critical_section(flags); |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhhost_freeio |
| * |
| * Description: |
| * Return an TD buffer to the free list |
| * |
| ****************************************************************************/ |
| |
| static void rx65n_usbhost_free_xfrinfo(struct |
| rx65n_usbhost_xfrinfo_s *xfrinfo) |
| { |
| struct rx65n_usbhost_list_s *node; |
| irqstate_t flags; |
| |
| /* Could be called from the interrupt level */ |
| |
| flags = enter_critical_section(); |
| node = (struct rx65n_usbhost_list_s *)xfrinfo; |
| node->flink = g_xfrfree; |
| g_xfrfree = node; |
| leave_critical_section(flags); |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_addctrled |
| * |
| * Description: |
| * Helper function to add an ED to the control list. |
| * |
| ****************************************************************************/ |
| |
| static inline int rx65n_usbhost_addctrled(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed) |
| { |
| /* Ctrl ED is always used and statically assigned */ |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_remctrled |
| * |
| * Description: |
| * Helper function remove an ED from the control list. |
| * |
| ****************************************************************************/ |
| |
| static inline int rx65n_usbhost_remctrled(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed) |
| { |
| /* Ctrl ED is always used and statically assigned */ |
| |
| /* The semaphore for the USB HID keyboard is released */ |
| |
| if (g_kbdport == g_usbidx) |
| { |
| nxsem_post(&g_rx65n_edlist[g_kbdpipe].wdhsem); |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_addbulked |
| * |
| * Description: |
| * Helper function to add an ED to the bulk list. |
| * |
| ****************************************************************************/ |
| |
| static inline int rx65n_usbhost_addbulked(struct rx65n_usbhost_s *priv, |
| const struct usbhost_epdesc_s *epdesc, |
| struct rx65n_usbhost_ed_s *ed) |
| { |
| #ifndef CONFIG_USBHOST_BULK_DISABLE |
| irqstate_t flags; |
| uint8_t pipe_no; |
| uint16_t pipe_cfg; |
| uint16_t pipe_maxp; |
| |
| /* Pipe Cycle Control Register is not needed for bulk pipe */ |
| |
| /* uint16_t pipe_peri; */ |
| |
| /* Disable bulk list processing while we modify the list */ |
| |
| flags = enter_critical_section(); |
| pipe_no = ed->pipenum; |
| |
| /* To begin with start config as Bulk pipe */ |
| |
| pipe_cfg = 0x4000; |
| |
| /* Update the direction */ |
| |
| /* Direction is in */ |
| |
| if (epdesc->in) |
| { |
| pipe_cfg = pipe_cfg & ~ (RX65N_USB_PIPECFG_DIR); |
| pipe_cfg = pipe_cfg | RX65N_USB_PIPECFG_SHTNAK; |
| } |
| else |
| { |
| pipe_cfg = pipe_cfg | (RX65N_USB_PIPECFG_DIR); |
| } |
| |
| /* Finally update the address */ |
| |
| pipe_cfg = pipe_cfg | ((epdesc->addr) & RX65N_USB_PIPECFG_EPNUM_MASK); |
| |
| /* Update pipe_maxp : Begin with initial value from device address */ |
| |
| pipe_maxp = rx65n_usbhost_getreg(RX65N_USB_DCPMAXP); |
| pipe_maxp &= ~(RX65N_USB_PIPEMAXP_MXPSMASK); |
| pipe_maxp = pipe_maxp | (epdesc->mxpacketsize); |
| |
| /* Now all the values are ready to be written */ |
| |
| g_usb_pipe_table[pipe_no].pipe_cfg = pipe_cfg; |
| g_usb_pipe_table[pipe_no].pipe_maxp = pipe_maxp; |
| g_usb_pipe_table[pipe_no].pipe_peri = 0; |
| |
| /* Now update these values in the requried pipe */ |
| |
| usb_cstd_pipe_init(pipe_no); |
| leave_critical_section(flags); |
| |
| return OK; |
| |
| #else |
| return -ENOSYS; |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_rembulked |
| * |
| * Description: |
| * Helper function remove an ED from the bulk list. |
| * |
| ****************************************************************************/ |
| |
| static inline int rx65n_usbhost_rembulked(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed) |
| { |
| /* This function requires implementation |
| * for OHCI specific interrupt disabling |
| * As RX65N is not OHCI compliant, this function |
| * need not be implemented |
| */ |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_getinterval |
| * |
| * Description: |
| * Convert the endpoint polling interval into a HCCA table increment |
| * |
| ****************************************************************************/ |
| |
| #if !defined(CONFIG_USBHOST_INT_DISABLE) || \ |
| !defined(CONFIG_USBHOST_ISOC_DISABLE) |
| static unsigned int rx65n_usbhost_getinterval(uint8_t interval) |
| { |
| /* The bInterval field of the endpoint descriptor contains the polling |
| * interval for interrupt and isochronous endpoints. For other types of |
| * endpoint, this value should be ignored. bInterval is provided in units |
| * of 1MS frames. |
| */ |
| |
| if (interval < 3) |
| { |
| return 2; |
| } |
| else if (interval < 7) |
| { |
| return 4; |
| } |
| else if (interval < 15) |
| { |
| return 8; |
| } |
| else if (interval < 31) |
| { |
| return 16; |
| } |
| else |
| { |
| return 32; |
| } |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_setinttab |
| * |
| * Description: |
| * Set the interrupt table to the selected value using the provided |
| * interval and offset. |
| * |
| ****************************************************************************/ |
| |
| #if !defined(CONFIG_USBHOST_INT_DISABLE) || \ |
| !defined(CONFIG_USBHOST_ISOC_DISABLE) |
| static void rx65n_usbhost_setinttab(uint32_t value, unsigned int interval, |
| unsigned int offset) |
| { |
| unsigned int i; |
| |
| for (i = offset; i < HCCA_INTTBL_WSIZE; i += interval) |
| { |
| HCCA->inttbl[i] = value; |
| } |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_addinted |
| * |
| * Description: |
| * Helper function to add an ED to the HCCA interrupt table. |
| * |
| * To avoid reshuffling the table so much and to keep life simple in |
| * general, the following rules are applied: |
| * |
| * 1. IN EDs get the even entries, OUT EDs get the odd entries. |
| * 2. Add IN/OUT EDs are scheduled together at the minimum interval of |
| * all IN/OUT EDs. |
| * |
| * This has the following consequences: |
| * |
| * 1. The minimum support polling rate is 2MS, and |
| * 2. Some devices may get polled at a much higher rate than they request |
| * |
| ****************************************************************************/ |
| |
| static inline int rx65n_usbhost_addinted(struct rx65n_usbhost_s *priv, |
| const struct usbhost_epdesc_s *epdesc, |
| struct rx65n_usbhost_ed_s *ed) |
| { |
| #ifndef CONFIG_USBHOST_INT_DISABLE |
| unsigned int interval; |
| unsigned int offset; |
| uint32_t head; |
| uint8_t pipe_no; |
| uint16_t pipe_cfg; |
| uint16_t pipe_maxp; |
| uint16_t pipe_peri; |
| |
| /* Get the quantized interval value associated with this ED and save it |
| * in the ED. |
| */ |
| |
| interval = rx65n_usbhost_getinterval(epdesc->interval); |
| |
| ed->interval = interval; |
| uinfo("interval: %d->%d\n", epdesc->interval, interval); |
| |
| /* Get the offset associated with the ED direction. IN EDs get the even |
| * entries, OUT EDs get the odd entries. |
| * |
| * Get the new, minimum interval. Add IN/OUT EDs are scheduled together |
| * at the minimum interval of all IN/OUT EDs. |
| */ |
| |
| if (epdesc->in) |
| { |
| offset = 0; |
| if (priv->ininterval > interval) |
| { |
| priv->ininterval = interval; |
| } |
| else |
| { |
| interval = priv->ininterval; |
| } |
| } |
| |
| else |
| { |
| offset = 1; |
| if (priv->outinterval > interval) |
| { |
| priv->outinterval = interval; |
| } |
| else |
| { |
| interval = priv->outinterval; |
| } |
| } |
| |
| uinfo("min interval: %d offset: %d\n", interval, offset); |
| |
| /* Get value for Interval Error Detection Interval */ |
| |
| pipe_no = ed->pipenum; |
| |
| if (epdesc->mxpacketsize != 1) |
| { |
| g_kbdpipe = pipe_no; |
| kbd_interrupt_in_pipe = pipe_no; |
| } |
| else |
| { |
| g_kbdpipe = 0; |
| } |
| |
| /* To begin with start config as INT pipe */ |
| |
| pipe_cfg = 0x8000; |
| |
| /* Update the direction */ |
| |
| /* Direction is in */ |
| |
| if (epdesc->in) |
| { |
| pipe_cfg = pipe_cfg & ~ (RX65N_USB_PIPECFG_DIR); |
| } |
| else |
| { |
| pipe_cfg = pipe_cfg | ~ (RX65N_USB_PIPECFG_DIR); |
| } |
| |
| /* Finally update the address */ |
| |
| pipe_cfg = pipe_cfg | ((epdesc->addr) & RX65N_USB_PIPECFG_EPNUM_MASK); |
| |
| /* Update pipe_maxp : Begin with initial value from endpoint address */ |
| |
| pipe_maxp = rx65n_usbhost_getreg(RX65N_USB_DCPMAXP); |
| pipe_maxp &= ~(RX65N_USB_PIPEMAXP_MXPSMASK); |
| pipe_maxp = pipe_maxp | (epdesc->mxpacketsize); |
| |
| /* Now get the Pipe Peri values */ |
| |
| pipe_peri = usb_hstd_get_pipe_peri_value(epdesc->interval); |
| |
| /* Now all the values are ready to be written */ |
| |
| g_usb_pipe_table[pipe_no].pipe_cfg = pipe_cfg; |
| g_usb_pipe_table[pipe_no].pipe_maxp = pipe_maxp; |
| g_usb_pipe_table[pipe_no].pipe_peri = pipe_peri; |
| |
| /* Now update these values in the requried pipe */ |
| |
| usb_cstd_pipe_init(pipe_no); |
| |
| struct rx65n_usbhost_xfrinfo_s *xfrinfo; |
| DEBUGASSERT(ed->xfrinfo == NULL); |
| |
| xfrinfo = rx65n_usbhost_alloc_xfrinfo(); |
| if (xfrinfo == NULL) |
| { |
| rx65n_usbhost_free_xfrinfo(xfrinfo); |
| uerr("ERROR: rx65n_usbhost_alloc_xfrinfo failed\n"); |
| return -ENOMEM; |
| } |
| |
| /* Initialize the transfer structure */ |
| |
| memset(xfrinfo, 0, sizeof(struct rx65n_usbhost_xfrinfo_s)); |
| xfrinfo->buffer = &kbd_report_data[0]; |
| xfrinfo->buflen = epdesc->mxpacketsize; |
| xfrinfo->tdxfercond = USB_DATARD; |
| |
| ed->xfrinfo = xfrinfo; |
| |
| /* Start the reading the interrupt pipe for KBD */ |
| |
| /* Now start the interrupt pipe */ |
| |
| usb_host_read_pipe_start(pipe_no); |
| |
| /* Get the head of the first of the duplicated entries. The first offset |
| * entry is always guaranteed to contain the common ED list head. |
| */ |
| |
| head = HCCA->inttbl[offset]; |
| |
| /* Clear all current entries in the interrupt table for this direction */ |
| |
| rx65n_usbhost_setinttab(0, 2, offset); |
| |
| /* Add the new ED before the old head of the periodic ED list and set the |
| * new ED as the head ED in all of the appropriate entries of the HCCA |
| * interrupt table. |
| */ |
| |
| ed->hw.nexted = head; |
| rx65n_usbhost_setinttab((uint32_t)ed, interval, offset); |
| uinfo("head: %08x next: %08x\n", ed, head); |
| |
| return OK; |
| #else |
| return -ENOSYS; |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_reminted |
| * |
| * Description: |
| * Helper function to remove an ED from the HCCA interrupt table. |
| * |
| * To avoid reshuffling the table so much and to keep life simple in |
| * general, the following rules are applied: |
| * |
| * 1. IN EDs get the even entries, OUT EDs get the odd entries. |
| * 2. Add IN/OUT EDs are scheduled together at the minimum interval of |
| * all IN/OUT EDs. |
| * |
| * This has the following consequences: |
| * |
| * 1. The minimum support polling rate is 2MS, and |
| * 2. Some devices may get polled at a much higher rate than they |
| * request. |
| * |
| ****************************************************************************/ |
| |
| static inline int rx65n_usbhost_reminted(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed) |
| { |
| /* This function is to disable OHCI specific interrupts |
| * As, RX65N is not OHCI compliant, this function does not require |
| * any implementation |
| */ |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_addisoced |
| * |
| * Description: |
| * Helper functions to add an ED to the periodic table. |
| * |
| ****************************************************************************/ |
| |
| static inline int rx65n_usbhost_addisoced(struct rx65n_usbhost_s *priv, |
| const struct usbhost_epdesc_s *epdesc, |
| struct rx65n_usbhost_ed_s *ed) |
| { |
| #ifndef CONFIG_USBHOST_ISOC_DISABLE |
| printf("Isochronous endpoints not yet supported\n"); |
| #endif |
| return -ENOSYS; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_remisoced |
| * |
| * Description: |
| * Helper functions to remove an ED from the periodic table. |
| * |
| ****************************************************************************/ |
| |
| static inline int rx65n_usbhost_remisoced(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed) |
| { |
| #ifndef CONFIG_USBHOST_ISOC_DISABLE |
| printf("Isochronous endpoints not yet supported\n"); |
| #endif |
| return -ENOSYS; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_enqueuetd |
| * |
| * Description: |
| * Enqueue a transfer descriptor. Notice that this function only supports |
| * queue on TD per ED. |
| * |
| ****************************************************************************/ |
| |
| static int rx65n_usbhost_enqueuetd(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed, |
| uint32_t dirpid, uint32_t toggle, |
| volatile uint8_t *buffer, size_t buflen) |
| { |
| struct rx65n_usbhost_gtd_s *td; |
| int ret = -ENOMEM; |
| |
| /* Allocate a TD from the free list */ |
| |
| /* Currently each TD would associate with one EP. So the epnumb |
| * is passed to tdalloc function and it would return the TD with |
| * this, there is no need to free this - there is no need |
| */ |
| |
| td = rx65n_usbhost_tdalloc(ed->pipenum); |
| |
| if (td != NULL) |
| { |
| /* Initialize allocated TD and link it before the common tail TD. */ |
| |
| td->hw.ctrl = (GTD_STATUS_R | dirpid | TD_DELAY(0) | |
| toggle | GTD_STATUS_CC_MASK); |
| TDTAIL->hw.ctrl = 0; |
| td->hw.cbp = (uint32_t)buffer; |
| TDTAIL->hw.cbp = 0; |
| td->hw.nexttd = (uint32_t)TDTAIL; |
| TDTAIL->hw.nexttd = 0; |
| td->hw.be = (uint32_t)(buffer + (buflen - 1)); |
| TDTAIL->hw.be = 0; |
| |
| /* Configure driver-only fields in the extended TD structure */ |
| |
| td->ed = ed; |
| |
| /* Link the td to the head of the ED's TD list */ |
| |
| ed->hw.headp = (uint32_t)td | ((ed->hw.headp) & ED_HEADP_C); |
| ed->hw.tailp = (uint32_t)TDTAIL; |
| ret = OK; |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_wdhwait |
| * |
| * Description: |
| * Set the request for the Writeback Done Head event well BEFORE enabling |
| * the transfer (as soon as we are absolutely committed to the to avoid |
| * transfer). We do this to minimize race conditions. This logic would |
| * have to be expanded if we want to have more than one packet in flight |
| * at a time! |
| * |
| ****************************************************************************/ |
| |
| static int rx65n_usbhost_wdhwait(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed) |
| { |
| struct rx65n_usbhost_xfrinfo_s *xfrinfo; |
| irqstate_t flags = enter_critical_section(); |
| int ret = -ENODEV; |
| |
| DEBUGASSERT(ed && ed->xfrinfo); |
| xfrinfo = ed->xfrinfo; |
| |
| /* Is the device still connected? */ |
| |
| if (priv->connected) |
| { |
| /* Yes.. then set wdhwait to indicate that we expect to be informed |
| * when either (1) the device is disconnected, or (2) the transfer |
| * completed. |
| */ |
| |
| xfrinfo->wdhwait = true; |
| ret = OK; |
| } |
| |
| leave_critical_section(flags); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_ctrltd |
| * |
| * Description: |
| * Process a IN or OUT request on the control endpoint. This function |
| * will enqueue the request and wait for it to complete. Only one |
| * transfer may be queued; Neither these methods nor the transfer() method |
| * can be called again until the control transfer functions returns. |
| * |
| * These are blocking methods; these functions will not return until the |
| * control transfer has completed. |
| * |
| ****************************************************************************/ |
| |
| static int rx65n_usbhost_ctrltd(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed, |
| uint32_t dirpid, uint8_t *buffer, |
| size_t buflen) |
| { |
| struct rx65n_usbhost_xfrinfo_s *xfrinfo; |
| uint32_t toggle; |
| int ret; |
| uint16_t sdata; |
| |
| /* Allocate a structure to retain the information needed when the |
| * transfer completes. |
| */ |
| |
| DEBUGASSERT(ed->xfrinfo == NULL); |
| |
| xfrinfo = rx65n_usbhost_alloc_xfrinfo(); |
| if (xfrinfo == NULL) |
| { |
| uerr("ERROR: rx65n_usbhost_alloc_xfrinfo failed\n"); |
| return -ENOMEM; |
| } |
| |
| /* Initialize the transfer structure */ |
| |
| memset(xfrinfo, 0, sizeof(struct rx65n_usbhost_xfrinfo_s)); |
| xfrinfo->buffer = buffer; |
| xfrinfo->buflen = buflen; |
| |
| /* copy the transfer condition - any way we are copying this |
| * address to ed just 2 lines after this... |
| */ |
| |
| xfrinfo->tdxfercond = ed->xfrinfo->tdxfercond; |
| |
| ed->xfrinfo = xfrinfo; |
| |
| /* Set the request for the Write-back Done Head event well BEFORE |
| * enabling the transfer. |
| */ |
| |
| if (priv->connected) |
| { |
| ret = rx65n_usbhost_wdhwait(priv, ed); |
| if (ret < 0) |
| { |
| syslog(LOG_INFO, "ERROR: Device disconnected\n"); |
| goto errout_with_xfrinfo; |
| } |
| } |
| |
| /* Configure the toggle field in the TD */ |
| |
| if (dirpid == GTD_STATUS_DP_SETUP) |
| { |
| toggle = GTD_STATUS_T_DATA0; |
| } |
| else |
| { |
| toggle = GTD_STATUS_T_DATA1; |
| } |
| |
| /* Then enqueue the transfer */ |
| |
| xfrinfo->tdstatus = TD_CC_NOERROR; |
| ret = rx65n_usbhost_enqueuetd(priv, ed, dirpid, toggle, buffer, buflen); |
| |
| if (ret == OK) |
| { |
| /* Set ControlListFilled. This bit is used to indicate whether there |
| * are TDs on the Control list. |
| */ |
| |
| if (dirpid == GTD_STATUS_DP_SETUP) |
| { |
| nxmutex_lock(&priv->lock); |
| |
| /* Set DATA0 bit of DCPCTR */ |
| |
| rx65n_usbhost_setbit(RX65N_USB_DCPCTR, RX65N_USB_DCPCTR_SQCLR); |
| |
| sdata = *(ed->xfrinfo->buffer) | (*(ed->xfrinfo->buffer + 1) << 8); |
| |
| /* Request type and Request */ |
| |
| rx65n_usbhost_putreg(sdata, RX65N_USB_USBREQ); |
| sdata = *(ed->xfrinfo->buffer + 2) | |
| (*(ed->xfrinfo->buffer + 3) << 8); |
| rx65n_usbhost_putreg(sdata, RX65N_USB_USBVAL); /* wValue */ |
| sdata = *(ed->xfrinfo->buffer + 4) | |
| (*(ed->xfrinfo->buffer + 5) << 8); |
| rx65n_usbhost_putreg(sdata, RX65N_USB_USBINDX); /* wIndex */ |
| sdata = *(ed->xfrinfo->buffer + 6) | |
| (*(ed->xfrinfo->buffer + 7) << 8); |
| rx65n_usbhost_putreg(sdata, RX65N_USB_USBLENG); /* wLen */ |
| |
| rx65n_usbhost_setbit(RX65N_USB_DCPCTR, RX65N_USB_DCPCTR_SQSET); |
| |
| hw_usb_hclear_sts_sign(); |
| hw_usb_hclear_sts_sack(); |
| |
| hw_usb_hset_enb_signe(); |
| hw_usb_hset_enb_sacke(); |
| hw_usb_hset_sureq(); |
| |
| /* At this point every thing is done w.r.t hardware to send the |
| * setup packet... Now release the exclusive access mutex and |
| * wait for wdhsem |
| */ |
| |
| nxmutex_unlock(&priv->lock); |
| |
| /* Wait for the Writeback Done Head interrupt */ |
| |
| if (priv->connected) |
| { |
| nxsem_wait_uninterruptible(&ed->wdhsem); |
| } |
| |
| /* Disable setup packet status response */ |
| |
| rx65n_usbhost_clearbit(RX65N_USB_INTENB1, |
| RX65N_USB_INTENB1_SACKE | RX65N_USB_INTENB1_SIGNE); |
| } |
| |
| else if (dirpid == GTD_STATUS_DP_IN) |
| { |
| nxmutex_lock(&priv->lock); |
| |
| /* BEMP0 Disable */ |
| |
| hw_usb_clear_bempenb(USB_PIPE0); |
| |
| /* BRDY0 Disable */ |
| |
| hw_usb_clear_brdyenb(USB_PIPE0); |
| |
| usb_hstd_ctrl_read_start(); |
| |
| /* At this point every thing is done w.r.t hardware to setup |
| * to receive the setup data... Now wait for interrupt |
| */ |
| |
| nxmutex_unlock(&priv->lock); |
| |
| if (priv->connected) |
| { |
| nxsem_wait_uninterruptible(&ed->wdhsem); |
| } |
| } |
| |
| else if (dirpid == GTD_STATUS_DP_OUT) |
| { |
| nxmutex_lock(&priv->lock); |
| |
| /* process setup packet status phase */ |
| |
| usb_hstd_ctrl_write_start(buffer, buflen); |
| |
| nxmutex_unlock(&priv->lock); |
| |
| if (priv->connected) |
| { |
| nxsem_wait_uninterruptible(&ed->wdhsem); |
| } |
| |
| /* Disable Empty Interrupt */ |
| |
| hw_usb_clear_bempenb(USB_PIPE0); |
| |
| /* Disable Not Ready Interrupt */ |
| |
| hw_usb_clear_nrdyenb(USB_PIPE0); |
| } |
| |
| /* Check the TD completion status bits */ |
| |
| if (xfrinfo->tdstatus == TD_CC_NOERROR) |
| { |
| ret = OK; |
| } |
| else |
| { |
| uerr("ERROR: Bad TD completion status: %d\n", xfrinfo->tdstatus); |
| ret = xfrinfo->tdstatus == TD_CC_STALL ? -EPERM : -EIO; |
| } |
| } |
| else |
| { |
| } |
| |
| /* Make sure that there is no outstanding request on this endpoint */ |
| |
| errout_with_xfrinfo: |
| rx65n_usbhost_free_xfrinfo(xfrinfo); |
| ed->xfrinfo = NULL; |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_usbinterrupt |
| * |
| * Description: |
| * USB interrupt handler |
| * |
| ****************************************************************************/ |
| |
| static int rx65n_usbhost_usbinterrupt(int irq, void *context, void *arg) |
| { |
| uint16_t intenb0; |
| uint16_t intenb1; |
| uint16_t intsts0; |
| uint16_t intsts1; |
| uint16_t ack_interrupt; |
| |
| /* Read Interrupt Status and mask out interrupts that are not enabled. */ |
| |
| intenb0 = rx65n_usbhost_getreg(RX65N_USB_INTENB0); |
| intenb1 = rx65n_usbhost_getreg(RX65N_USB_INTENB1); |
| intsts0 = rx65n_usbhost_getreg(RX65N_USB_INTSTS0); |
| intsts1 = rx65n_usbhost_getreg(RX65N_USB_INTSTS1); |
| |
| if ((((intsts1 & intenb1) & RX65N_USB_INTSTS1_SACK)) == |
| RX65N_USB_INTSTS1_SACK) |
| { |
| hw_usb_hclear_sts_sack(); |
| |
| /* Disable setup packet status response */ |
| |
| rx65n_usbhost_clearbit(RX65N_USB_INTENB1, RX65N_USB_INTENB1_SACKE | |
| RX65N_USB_INTENB1_SIGNE); |
| |
| /* release the EP0 ep->wdhsem semaphore */ |
| |
| DEBUGASSERT(work_available(&g_usbhost.rx65n_interrupt_bhalf)); |
| |
| DEBUGVERIFY(work_queue(HPWORK, &g_usbhost.rx65n_interrupt_bhalf, |
| rx65n_usbhost_bottomhalf, |
| (void *)USB_PROCESS_SACK_INT, 0)); |
| } |
| |
| /* Is it ERROR for Setup packet... */ |
| |
| else if ((((intsts1 & intenb1) & RX65N_USB_INTSTS1_SIGN)) == |
| RX65N_USB_INTSTS1_SIGN) |
| { |
| hw_usb_hclear_sts_sign(); |
| |
| /* Disable setup packet status response */ |
| |
| rx65n_usbhost_clearbit(RX65N_USB_INTENB1, RX65N_USB_INTENB1_SACKE | |
| RX65N_USB_INTENB1_SIGNE); |
| DEBUGASSERT(work_available(&g_usbhost.rx65n_interrupt_bhalf)); |
| |
| DEBUGVERIFY(work_queue(HPWORK, &g_usbhost.rx65n_interrupt_bhalf, |
| rx65n_usbhost_bottomhalf, |
| (void *)USB_PROCESS_SIGN_INT, 0)); |
| } |
| |
| /* Check for EOFERR interrupt... Not using it though */ |
| |
| else if ((((intsts1 & intenb1) & RX65N_USB_INTSTS1_EOFERR)) == |
| RX65N_USB_INTSTS1_EOFERR) |
| { |
| rx65n_usbhost_putreg(((~RX65N_USB_INTSTS1_EOFERR) & |
| INTSTS1_BIT_VALUES_TO_ACK), RX65N_USB_INTSTS1); |
| } |
| |
| /* Check for over current condition... */ |
| |
| else if ((((intsts1 & intenb1) & RX65N_USB_INTSTS1_OVRCRE)) == |
| RX65N_USB_INTSTS1_OVRCRE) |
| { |
| ack_interrupt = INTSTS1_BIT_VALUES_TO_ACK & |
| (~(RX65N_USB_INTSTS1_OVRCRE)); |
| |
| /* Acknowledge the OVRCR interrupt */ |
| |
| rx65n_usbhost_putreg(ack_interrupt, RX65N_USB_INTSTS1); |
| } |
| |
| /* Check for attach condition... */ |
| |
| else if ((((intsts1 & intenb1) & RX65N_USB_INTSTS1_ATTCH)) == |
| RX65N_USB_INTSTS1_ATTCH) |
| { |
| hw_usb_hclear_sts_attch(); |
| |
| usb_hstd_bus_int_disable(); |
| DEBUGASSERT(work_available(&g_usbhost.rx65n_interrupt_bhalf)); |
| |
| DEBUGVERIFY(work_queue(HPWORK, &g_usbhost.rx65n_interrupt_bhalf, |
| rx65n_usbhost_bottomhalf, |
| (void *)USB_PROCESS_ATTACHED_INT, 0)); |
| } |
| |
| /* Check for detach condition... */ |
| |
| else if ((((intsts1 & intenb1) & RX65N_USB_INTSTS1_DTCH)) == |
| RX65N_USB_INTSTS1_DTCH) |
| { |
| hw_usb_hclear_sts_dtch(); |
| |
| usb_hstd_bus_int_disable(); |
| DEBUGASSERT(work_available(&g_usbhost.rx65n_interrupt_bhalf)); |
| |
| DEBUGVERIFY(work_queue(HPWORK, &g_usbhost.rx65n_interrupt_bhalf, |
| rx65n_usbhost_bottomhalf, |
| (void *)USB_PROCESS_DETACHED_INT, 0)); |
| } |
| |
| /* Check for BCHG interrupt... Not using it though */ |
| |
| else if ((((intsts1 & intenb1) & RX65N_USB_INTSTS1_BCHG)) == |
| RX65N_USB_INTSTS1_BCHG) |
| { |
| hw_usb_hclear_sts_bchg(); |
| hw_usb_hclear_enb_bchge(); |
| } |
| |
| /* Check for BRDY interrupt... */ |
| |
| else if ((((intsts0 & intenb0) & RX65N_USB_INTSTS0_BRDY)) == |
| RX65N_USB_INTSTS0_BRDY) |
| { |
| /* Schedule the workque for processing */ |
| |
| DEBUGASSERT(work_available(&g_usbhost.rx65n_interrupt_bhalf)); |
| |
| DEBUGVERIFY(work_queue(HPWORK, &g_usbhost.rx65n_interrupt_bhalf, |
| rx65n_usbhost_bottomhalf, |
| (void *)USB_PROCESS_BRDY_INT, 0)); |
| } |
| |
| /* Check for BEMP interrupt... */ |
| |
| else if ((((intsts0 & intenb0) & RX65N_USB_INTSTS0_BEMP)) == |
| RX65N_USB_INTSTS0_BEMP) |
| { |
| /* Schedule the workque for processing */ |
| |
| DEBUGASSERT(work_available(&g_usbhost.rx65n_interrupt_bhalf)); |
| |
| DEBUGVERIFY(work_queue(HPWORK, &g_usbhost.rx65n_interrupt_bhalf, |
| rx65n_usbhost_bottomhalf, |
| (void *)USB_PROCESS_BEMP_INT, 0)); |
| } |
| |
| /* Check for NRDY interrupt... */ |
| |
| else if ((((intsts0 & intenb0) & RX65N_USB_INTSTS0_NRDY)) == |
| RX65N_USB_INTSTS0_NRDY) |
| { |
| /* Schedule the workque for processing */ |
| |
| DEBUGASSERT(work_available(&g_usbhost.rx65n_interrupt_bhalf)); |
| |
| DEBUGVERIFY(work_queue(HPWORK, &g_usbhost.rx65n_interrupt_bhalf, |
| rx65n_usbhost_bottomhalf, |
| (void *)USB_PROCESS_NRDY_INT, 0)); |
| } |
| |
| /* Check for SOF interrupt... Not using though */ |
| |
| else if ((((intsts0 & intenb0) & RX65N_USB_INTSTS0_SOFR)) == |
| RX65N_USB_INTSTS0_SOFR) |
| { |
| hw_usb_clear_sts_sofr(); |
| } |
| |
| /* Check for VBINT interrupt... Not using though */ |
| |
| else if ((((intsts0 & intenb0) & RX65N_USB_INTSTS0_VBINT)) == |
| RX65N_USB_INTSTS0_VBINT) |
| { |
| rx65n_usbhost_clearbit(RX65N_USB_INTSTS0, RX65N_USB_INTSTS0_VBINT); |
| } |
| |
| /* If none of the interrupt - what happens? */ |
| |
| else |
| { |
| syslog(LOG_INFO, "Unhandled interrupt. INTENB0 = 0x%x, \ |
| INTENB1 = 0x%x, INTSTS0 = 0x%x and INTSTS1 = 0x%x\n", |
| intenb0, intenb1, intsts0, intsts1); |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_bottomhalf |
| * |
| * Description: |
| * OHCI interrupt bottom half. This function runs on the high priority |
| * worker thread and was xcheduled when the last interrupt occurred. |
| * Possibly this acts as host_thread functionality which is present in |
| * FIT driver |
| ****************************************************************************/ |
| |
| static void rx65n_usbhost_bottomhalf(void *arg) |
| { |
| struct rx65n_usbhost_s *priv = &g_usbhost; |
| uint32_t bottom_half_processing = (uint32_t)arg; |
| uint16_t device_speed; |
| |
| /* connected_times variable is used to check the number of times */ |
| |
| /* connected and disconnected */ |
| |
| static uint32_t connected_times = 0; |
| |
| /* We need to have exclusive access to the OHCI data structures. |
| * Waiting here is not a good thing to do on the worker thread, but there |
| * is no real option (other than to reschedule and delay). |
| */ |
| |
| nxmutex_lock(&g_usbhost.lock); |
| |
| if (bottom_half_processing == USB_PROCESS_SACK_INT) |
| { |
| EDCTRL->xfrinfo->tdstatus = TD_CC_NOERROR; |
| hw_usb_hclear_sts_sack(); |
| nxsem_post(&EDCTRL->wdhsem); |
| } |
| |
| else if (bottom_half_processing == USB_PROCESS_SIGN_INT) |
| { |
| EDCTRL->xfrinfo->tdstatus = TD_CC_PIDCHECKFAILURE; |
| hw_usb_hclear_sts_sign(); |
| nxsem_post(&EDCTRL->wdhsem); |
| } |
| |
| else if (bottom_half_processing == USB_PROCESS_ATTACHED_INT) |
| { |
| g_attached = true; |
| device_speed = usb_hstd_attach_process(); |
| if (!priv->connected) |
| { |
| /* Yes.. connected. */ |
| |
| connected_times++; |
| syslog(LOG_INFO, "NuttX: USB Device Connected. %d\n", |
| connected_times); |
| priv->connected = true; |
| priv->change = true; |
| |
| /* Update the speed of the connected device */ |
| |
| if (device_speed == USB_ATTACHL) |
| { |
| g_usbhost.rhport.hport.speed = USB_SPEED_LOW; |
| } |
| else |
| { |
| g_usbhost.rhport.hport.speed = USB_SPEED_FULL; |
| } |
| |
| /* Notify any waiters */ |
| |
| if (priv->pscwait) |
| { |
| priv->pscwait = false; |
| nxsem_post(&priv->pscsem); |
| } |
| } |
| else |
| { |
| syslog(LOG_INFO, "WARNING: Spurious status change (connected)\n"); |
| } |
| |
| g_attached = false; |
| } |
| |
| else if (bottom_half_processing == USB_PROCESS_DETACHED_INT) |
| { |
| g_detached = true; |
| device_speed = usb_hstd_detach_process(); |
| |
| if (priv->connected) |
| { |
| /* Yes.. disconnect the device */ |
| |
| syslog(LOG_INFO, "NuttX: USB Device Disconnected. %d\n", |
| connected_times); |
| priv->connected = false; |
| priv->change = true; |
| |
| /* As detach calls several free functions, make sure access to */ |
| |
| /* hardware is available */ |
| |
| nxmutex_unlock(&g_usbhost.lock); |
| |
| /* Are we bound to a class instance? */ |
| |
| if (g_kbdpipe) |
| { |
| nxsem_post(&g_rx65n_edlist[g_kbdpipe].wdhsem); |
| } |
| |
| g_kbdpipe = 0; |
| if (priv->rhport.hport.devclass) |
| { |
| /* Yes.. Disconnect the class */ |
| |
| CLASS_DISCONNECTED(priv->rhport.hport.devclass); |
| priv->rhport.hport.devclass = NULL; |
| } |
| |
| /* Notify any waiters for the Root Hub Status change event */ |
| |
| if (priv->pscwait) |
| { |
| nxsem_post(&priv->pscsem); |
| priv->pscwait = false; |
| } |
| |
| return; |
| } |
| else |
| { |
| syslog(LOG_INFO, "WARNING: Spurious status change \ |
| (disconnected)\n"); |
| } |
| |
| g_detached = false; |
| } |
| |
| else if (bottom_half_processing == USB_PROCESS_BRDY_INT) |
| { |
| usb_hstd_brdy_pipe(); |
| } |
| |
| else if (bottom_half_processing == USB_PROCESS_BEMP_INT) |
| { |
| usb_hstd_bemp_pipe(); |
| } |
| |
| else if (bottom_half_processing == USB_PROCESS_NRDY_INT) |
| { |
| usb_hstd_nrdy_pipe(); |
| } |
| |
| /* If the bottom half is not any of the above then log the |
| * reason for bottom half being called |
| */ |
| |
| else |
| { |
| nxsig_usleep(100); |
| uwarn("WARNING: un known bottomhalf. Value is %d\n", |
| bottom_half_processing); |
| syslog(LOG_INFO, "WARNING: un known bottomhalf. Value is %d\n", |
| bottom_half_processing); |
| } |
| |
| nxmutex_unlock(&g_usbhost.lock); |
| } |
| |
| /**************************************************************************** |
| * USB Host Controller Operations |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_wait |
| * |
| * Description: |
| * Wait for a device to be connected or disconnected to/from a hub port. |
| * |
| * Input Parameters: |
| * conn - The USB host connection instance obtained as a parameter from |
| * the call to the USB driver initialization logic. |
| * hport - The location to return the hub port descriptor that detected |
| * the connection related event. |
| * |
| * Returned Value: |
| * Zero (OK) is returned on success when a device is connected or |
| * disconnected. This function will not return until either (1) a device |
| * is connected or disconnect to/from any hub port or until (2) some |
| * failure occurs. On a failure, a negated errno value is returned |
| * indicating the nature of the failure |
| * |
| * Assumptions: |
| * - Called from a single thread so no mutual exclusion is required. |
| * - Never called from an interrupt handler. |
| * |
| ****************************************************************************/ |
| |
| static int rx65n_usbhost_wait(struct usbhost_connection_s *conn, |
| struct usbhost_hubport_s **hport) |
| { |
| struct rx65n_usbhost_s *priv = (struct rx65n_usbhost_s *)&g_usbhost; |
| struct usbhost_hubport_s *connport; |
| irqstate_t flags; |
| int ret; |
| |
| flags = enter_critical_section(); |
| for (; ; ) |
| { |
| /* Is there a change in the connection state of the single root hub |
| * port? |
| */ |
| |
| if (priv->change) |
| { |
| connport = &priv->rhport.hport; |
| priv->change = false; |
| |
| /* Yes.. check for false alarms */ |
| |
| if (priv->connected != connport->connected) |
| { |
| /* Not a false alarm.. Remember the new state */ |
| |
| connport->connected = priv->connected; |
| |
| /* And return the root hub port */ |
| |
| *hport = connport; |
| leave_critical_section(flags); |
| |
| uinfo("RHport Connected: %s\n", |
| connport->connected ? "YES" : "NO"); |
| |
| return OK; |
| } |
| } |
| |
| #ifdef CONFIG_USBHOST_HUB |
| /* Is a device connected to an external hub? */ |
| |
| if (priv->hport) |
| { |
| /* Yes.. return the external hub port */ |
| |
| connport = (struct usbhost_hubport_s *)priv->hport; |
| priv->hport = NULL; |
| |
| *hport = connport; |
| leave_critical_section(flags); |
| |
| uinfo("Hub port Connected: %s\n", |
| connport->connected ? "YES" : "NO"); |
| return OK; |
| } |
| |
| #endif |
| /* Wait for the next connection event */ |
| |
| priv->pscwait = true; |
| ret = nxsem_wait_uninterruptible(&priv->pscsem); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_enumerate |
| * |
| * Description: |
| * Enumerate the connected device. As part of this enumeration process, |
| * the driver will (1) get the device's configuration descriptor, (2) |
| * extract the class ID info from the configuration descriptor, (3) call |
| * usbhost_findclass() to find the class that supports this device, (4) |
| * call the create() method on the struct usbhost_registry_s interface |
| * to get a class instance, and finally (5) call the connect() method |
| * of the struct usbhost_class_s interface. After that, the class is in |
| * charge of the sequence of operations. |
| * |
| * Input Parameters: |
| * conn - The USB host connection instance obtained as a parameter from |
| * the call to the USB driver initialization logic. |
| * hport - The descriptor of the hub port that has the newly connected |
| * device. |
| * |
| * 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 int rx65n_usbhost_rh_enumerate(struct usbhost_connection_s *conn, |
| struct usbhost_hubport_s *hport) |
| { |
| struct rx65n_usbhost_s *priv = (struct rx65n_usbhost_s *)&g_usbhost; |
| DEBUGASSERT(conn != NULL && hport != NULL && hport->port == 0); |
| |
| /* Are we connected to a device? The caller should have called the wait() |
| * method first to be assured that a device is connected. |
| */ |
| |
| while (!priv->connected) |
| { |
| /* No, return an error */ |
| |
| uwarn("WARNING: Not connected\n"); |
| |
| return -ENODEV; |
| } |
| |
| /* USB 2.0 spec says at least 50ms delay before port reset */ |
| |
| nxsig_usleep(100 * 1000); |
| |
| /* Put RH port 1 in reset. |
| * Currently supporting only single downstream port) |
| */ |
| |
| nxsig_usleep(200 * 1000); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_enumerate |
| * |
| * Description: |
| * Enumerate the connected device. As part of this enumeration process, |
| * the driver will (1) get the device's configuration descriptor, (2) |
| * extract the class ID info from the configuration descriptor, (3) call |
| * usbhost_findclass() to find the class that supports this device, (4) |
| * call the create() method on the struct usbhost_registry_s interface |
| * to get a class instance, and finally (5) call the connect() method |
| * of the struct usbhost_class_s interface. After that, the class is in |
| * charge of the sequence of operations. |
| * |
| * Input Parameters: |
| * conn - The USB host connection instance obtained as a parameter from |
| * the call to the USB driver initialization logic. |
| * hport - The descriptor of the hub port that has the newly connected |
| * device. |
| * |
| * 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 int rx65n_usbhost_enumerate(struct usbhost_connection_s *conn, |
| struct usbhost_hubport_s *hport) |
| { |
| int ret; |
| |
| DEBUGASSERT(hport); |
| |
| /* If this is a connection on the root hub, then we need to go to |
| * little more effort to get the device speed. If it is a connection |
| * on an external hub, then we already have that information. |
| */ |
| |
| #ifdef CONFIG_USBHOST_HUB |
| if (ROOTHUB(hport)) |
| #endif |
| { |
| ret = rx65n_usbhost_rh_enumerate(conn, hport); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| } |
| |
| /* Then let the common usbhost_enumerate do the real enumeration. */ |
| |
| uinfo("Enumerate the device\n"); |
| |
| ret = usbhost_enumerate(hport, &hport->devclass); |
| if (ret < 0) |
| { |
| uerr("ERROR: Enumeration failed: %d\n", ret); |
| syslog(LOG_INFO, "ERROR: Enumeration failed: %d\n", ret); |
| } |
| |
| else |
| { |
| syslog(LOG_INFO, "Root Hub Port device enumerated"); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_ep0configure |
| * |
| * Description: |
| * Configure endpoint 0. This method is normally used internally by the |
| * enumerate() method but is made available at the interface to support |
| * an external implementation of the enumeration logic. |
| * |
| * Input Parameters: |
| * drvr - The USB host driver instance obtained as a parameter from |
| * the call to the class create() method. |
| * ep0 - The (opaque) EP0 endpoint instance |
| * funcaddr - The USB address of the function containing the endpoint |
| * that EP0 controls |
| * speed - The speed of the port USB_SPEED_LOW, _FULL, or _HIGH |
| * mps (maxpacketsize) - The maximum number of bytes that can be sent |
| * to or received from the endpoint in a single data packet |
| * |
| * 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 int rx65n_usbhost_ep0configure(struct usbhost_driver_s *drvr, |
| usbhost_ep_t ep0, |
| uint8_t funcaddr, uint8_t speed, |
| uint16_t maxpacketsize) |
| { |
| struct rx65n_usbhost_s *priv = (struct rx65n_usbhost_s *)drvr; |
| uint16_t current_dcpmaxp; |
| |
| DEBUGASSERT(drvr != NULL && ep0 != NULL && |
| funcaddr < 128 && maxpacketsize < 2048); |
| |
| /* We must have exclusive access to EP0 and the control list */ |
| |
| nxmutex_lock(&priv->lock); |
| usb_cstd_set_nak(USB_PIPE0); |
| |
| /* Make sure, all the DEVADDn registers are set to default state */ |
| |
| /* if (funcaddr == 0) */ |
| |
| hw_usb_hset_usbspd(funcaddr, (speed << 6)); |
| |
| /* else |
| * hw_usb_hset_usbspd (funcaddr, (1 << 6)); |
| * debug_ptr = RX65N_USB_DEVADD0+funcaddr; |
| */ |
| |
| current_dcpmaxp = hw_usb_read_dcpmaxp(); |
| current_dcpmaxp = current_dcpmaxp & (~RX65N_USB_DCPMAXP_MXPS_MASK); |
| current_dcpmaxp |= maxpacketsize; |
| current_dcpmaxp = current_dcpmaxp & (~RX65N_USB_DCPMAXP_DEVADDR_MASK); |
| current_dcpmaxp |= funcaddr << RX65N_USB_DCPMAXP_DEVADDR_SHIFT; |
| hw_usb_write_dcpmxps(current_dcpmaxp); |
| |
| hw_usb_set_curpipe(USB_CUSE, USB_PIPE0); |
| hw_usb_set_bclr(USB_CUSE); |
| |
| nxmutex_unlock(&priv->lock); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_epalloc |
| * |
| * Description: |
| * Allocate and configure one endpoint. |
| * |
| * Input Parameters: |
| * drvr - The USB host driver instance obtained as a parameter from the |
| * call to the class create() method. |
| * epdesc - Describes the endpoint to be allocated. |
| * ep - A memory location provided by the caller in which to receive the |
| * allocated endpoint 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 int rx65n_usbhost_epalloc(struct usbhost_driver_s *drvr, |
| const struct usbhost_epdesc_s *epdesc, |
| usbhost_ep_t *ep) |
| { |
| struct rx65n_usbhost_s *priv = (struct rx65n_usbhost_s *)drvr; |
| struct usbhost_hubport_s *hport; |
| struct rx65n_usbhost_ed_s *ed; |
| int ret = -ENOMEM; |
| uint8_t pipe_num; |
| uint8_t pipe_type; |
| uint8_t pipe_dir; |
| |
| /* Sanity check. NOTE that this method should only be called if a |
| * device is connected (because we need a valid low speed indication). |
| */ |
| |
| DEBUGASSERT(priv && epdesc && ep && priv->connected); |
| |
| /* We must have exclusive access to the ED pool, the bulk list, the |
| * periodic list and the interrupt table. |
| */ |
| |
| nxmutex_lock(&priv->lock); |
| |
| /* Take the ED descriptor from the list of ED Array - based on pipe num |
| * Also note it down as part of ED structurie itself |
| * for futer use - if needed |
| * Take the next ED from the beginning of the free list |
| */ |
| |
| /* DEBUGASSERT(pipe_no == USB_NULL); */ |
| |
| if (epdesc->in) |
| { |
| pipe_dir = USB_EP_IN; |
| } |
| else |
| { |
| pipe_dir = USB_EP_OUT; |
| } |
| |
| if ((epdesc->xfrtype) == USB_EP_ATTR_XFER_CONTROL) |
| { |
| pipe_type = USB_EP_CTRL; |
| } |
| else if ((epdesc->xfrtype) == USB_EP_ATTR_XFER_BULK) |
| { |
| pipe_type = USB_EP_BULK; |
| } |
| else if ((epdesc->xfrtype) == USB_EP_ATTR_XFER_INT) |
| { |
| pipe_type = USB_EP_INT; |
| } |
| else |
| { |
| pipe_type = USB_EP_ISO; |
| } |
| |
| pipe_num = usb_hstd_get_pipe_no(pipe_type, pipe_dir); |
| DEBUGASSERT(pipe_no == USB_NULL); |
| g_usb_pipe_table[pipe_num].use_flag = USB_TRUE; |
| |
| ed = &g_rx65n_edlist[pipe_num]; |
| if (ed) |
| { |
| /* Remove the ED from the freelist */ |
| |
| /* Configure the endpoint descriptor. */ |
| |
| memset((void *)ed, 0, sizeof(struct rx65n_usbhost_ed_s)); |
| |
| hport = epdesc->hport; |
| ed->hw.ctrl = (uint32_t)(hport->funcaddr) << ED_CONTROL_FA_SHIFT | |
| (uint32_t)(epdesc->addr) << ED_CONTROL_EN_SHIFT | |
| (uint32_t)(epdesc->mxpacketsize) << ED_CONTROL_MPS_SHIFT; |
| |
| /* Note down the pipe number for reference */ |
| |
| ed->pipenum = pipe_num; |
| |
| /* Get the direction of the endpoint. For control endpoints, the |
| * direction is in the TD. |
| */ |
| |
| if (epdesc->xfrtype == USB_EP_ATTR_XFER_CONTROL) |
| { |
| ed->hw.ctrl |= ED_CONTROL_D_TD1; |
| } |
| else if (epdesc->in) |
| { |
| ed->hw.ctrl |= ED_CONTROL_D_IN; |
| } |
| else |
| { |
| ed->hw.ctrl |= ED_CONTROL_D_OUT; |
| } |
| |
| /* Check for a low-speed device */ |
| |
| if (hport->speed == USB_SPEED_LOW) |
| { |
| ed->hw.ctrl |= ED_CONTROL_S; |
| } |
| |
| /* Set the transfer type */ |
| |
| ed->xfrtype = epdesc->xfrtype; |
| |
| /* Special Case isochronous transfer types */ |
| |
| uinfo("EP%d CTRL:%08x\n", epdesc->addr, ed->hw.ctrl); |
| nxsem_init(&ed->wdhsem, 0, 0); |
| |
| /* Link the common tail TD to the ED's TD list */ |
| |
| ed->hw.headp = (uint32_t)TDTAIL; |
| ed->hw.tailp = (uint32_t)TDTAIL; |
| |
| /* Now add the endpoint descriptor to the appropriate list */ |
| |
| switch (ed->xfrtype) |
| { |
| case USB_EP_ATTR_XFER_CONTROL: |
| ret = rx65n_usbhost_addctrled(priv, ed); |
| break; |
| |
| case USB_EP_ATTR_XFER_BULK: |
| ret = rx65n_usbhost_addbulked(priv, epdesc, ed); |
| break; |
| |
| case USB_EP_ATTR_XFER_INT: |
| ret = rx65n_usbhost_addinted(priv, epdesc, ed); |
| break; |
| |
| case USB_EP_ATTR_XFER_ISOC: |
| ret = rx65n_usbhost_addisoced(priv, epdesc, ed); |
| break; |
| |
| default: |
| ret = -EINVAL; |
| break; |
| } |
| |
| /* Was the ED successfully added? */ |
| |
| if (ret < 0) |
| { |
| /* No.. destroy it and report the error */ |
| |
| uerr("ERROR: Failed to queue ED for transfer type: %d\n", |
| ed->xfrtype); |
| nxsem_destroy(&ed->wdhsem); |
| rx65n_usbhost_edfree(ed); |
| } |
| else |
| { |
| /* Yes.. return an opaque reference to the ED */ |
| |
| *ep = (usbhost_ep_t)ed; |
| } |
| } |
| |
| nxmutex_unlock(&priv->lock); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_epfree |
| * |
| * Description: |
| * Free and endpoint previously allocated by DRVR_EPALLOC. |
| * |
| * Input Parameters: |
| * drvr - The USB host driver instance obtained as a parameter from the |
| * call to the class create() method. |
| * ep - The endpint to be freed. |
| * |
| * 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 int rx65n_usbhost_epfree(struct usbhost_driver_s *drvr, |
| usbhost_ep_t ep) |
| { |
| struct rx65n_usbhost_s *priv = (struct rx65n_usbhost_s *)drvr; |
| struct rx65n_usbhost_ed_s *ed = (struct rx65n_usbhost_ed_s *)ep; |
| int ret; |
| |
| /* There should not be any pending, real TDs linked to this ED */ |
| |
| DEBUGASSERT(ed && (ed->hw.headp & ED_HEADP_ADDR_MASK) == TDTAIL_ADDR); |
| |
| /* We must have exclusive access to the ED pool, the bulk list, |
| * the periodic list and the interrupt table. |
| */ |
| |
| nxmutex_lock(&priv->lock); |
| |
| /* Remove the ED to the correct list depending on the trasfer type */ |
| |
| switch (ed->xfrtype) |
| { |
| case USB_EP_ATTR_XFER_CONTROL: |
| ret = rx65n_usbhost_remctrled(priv, ed); |
| break; |
| |
| case USB_EP_ATTR_XFER_BULK: |
| ret = rx65n_usbhost_rembulked(priv, ed); |
| break; |
| |
| case USB_EP_ATTR_XFER_INT: |
| ret = rx65n_usbhost_reminted(priv, ed); |
| break; |
| |
| case USB_EP_ATTR_XFER_ISOC: |
| ret = rx65n_usbhost_remisoced(priv, ed); |
| break; |
| |
| default: |
| ret = -EINVAL; |
| break; |
| } |
| |
| /* Put the ED back into the free list */ |
| |
| rx65n_usbhost_edfree(ed); |
| nxmutex_unlock(&priv->lock); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_alloc |
| * |
| * Description: |
| * Some hardware supports special memory in which request and descriptor |
| * data can be accessed more efficiently. This method provides a mechanism |
| * to allocate the request/descriptor memory. If the underlying hardware |
| * does not support such "special" memory, this functions may |
| * simply map to kmm_malloc. |
| * |
| * This interface was optimized under a particular assumption. It was |
| * assumed that the driver maintains a pool of small, pre-allocated buffers |
| * for descriptor traffic. NOTE that size is not an input, but an output: |
| * The size of the pre-allocated buffer is returned. |
| * |
| * Input Parameters: |
| * drvr - The USB host driver instance obtained as a parameter from the |
| * call to the class create() method. |
| * buffer - The address of a memory location provided by the caller in |
| * which to return the allocated buffer memory address. |
| * maxlen - The address of a memory location provided by the caller in |
| * which to return the maximum size of the allocated buffer memory. |
| * |
| * Returned Value: |
| * On success, zero (OK) is returned. On a failure, a negated errno value i |
| * returned indicating the nature of the failure |
| * |
| * Assumptions: |
| * - Called from a single thread so no mutual exclusion is required. |
| * - Never called from an interrupt handler. |
| * |
| ****************************************************************************/ |
| |
| static int rx65n_usbhost_alloc(struct usbhost_driver_s *drvr, |
| uint8_t **buffer, size_t *maxlen) |
| { |
| struct rx65n_usbhost_s *priv = (struct rx65n_usbhost_s *)drvr; |
| |
| DEBUGASSERT(priv && buffer && maxlen); |
| int ret = -ENOMEM; |
| |
| /* We must have exclusive access to the transfer buffer pool */ |
| |
| nxmutex_lock(&priv->lock); |
| |
| *buffer = rx65n_usbhost_tballoc(); |
| if (*buffer) |
| { |
| *maxlen = CONFIG_RX65N_USBHOST_TDBUFSIZE; |
| ret = OK; |
| } |
| |
| nxmutex_unlock(&priv->lock); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_free |
| * |
| * Description: |
| * Some hardware supports special memory in which request and descriptor |
| * data can be accessed more efficiently. This method provides a |
| * mechanism to free that request/descriptor memory. If the underlying |
| * hardware does not support such "special" memory, this functions may |
| * simply map to kmm_free(). |
| * |
| * Input Parameters: |
| * drvr - The USB host driver instance obtained as a parameter from the |
| * call to the class create() method. |
| * buffer - The address of the allocated buffer memory to be freed. |
| * |
| * Returned Value: |
| * On success, zero (OK) is returned. On a failure, a negated errno |
| * value is returned indicating the nature of the failure |
| * |
| * Assumptions: |
| * - Never called from an interrupt handler. |
| * |
| ****************************************************************************/ |
| |
| static int rx65n_usbhost_free(struct usbhost_driver_s *drvr, uint8_t *buffer) |
| { |
| struct rx65n_usbhost_s *priv = (struct rx65n_usbhost_s *)drvr; |
| DEBUGASSERT(buffer); |
| |
| /* We must have exclusive access to the transfer buffer pool */ |
| |
| nxmutex_lock(&priv->lock); |
| rx65n_usbhost_tbfree(buffer); |
| nxmutex_unlock(&priv->lock); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_ioalloc |
| * |
| * Description: |
| * Some hardware supports special memory in which larger IO buffers can |
| * be accessed more efficiently. This method provides a mechanism to |
| * allocate the request/descriptor memory. If the underlying hardware |
| * does not support such "special" memory, this functions may simply map |
| * to kmm_malloc. |
| * |
| * This interface differs from DRVR_ALLOC in that the buffers are |
| * variable-sized. |
| * |
| * Input Parameters: |
| * drvr - The USB host driver instance obtained as a parameter from the |
| * call to the class create() method. |
| * buffer - The address of a memory location provided by the caller in |
| * which to return the allocated buffer memory address. |
| * buflen - The size of the buffer required. |
| * |
| * Returned Value: |
| * On success, zero (OK) is returned. On a failure, a negated errno |
| * returned indicating the nature of the failure |
| * value is |
| * |
| * Assumptions: |
| * This function will *not* be called from an interrupt handler. |
| * |
| ****************************************************************************/ |
| |
| static int rx65n_usbhost_ioalloc(struct usbhost_driver_s *drvr, |
| uint8_t **buffer, size_t buflen) |
| { |
| uint8_t *alloc; |
| |
| DEBUGASSERT(drvr && buffer && buflen > 0); |
| |
| /* There is no special memory requirement */ |
| |
| alloc = kmm_malloc(buflen); |
| if (!alloc) |
| { |
| return -ENOMEM; |
| } |
| |
| /* Return the allocated buffer */ |
| |
| *buffer = alloc; |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_iofree |
| * |
| * Description: |
| * Some hardware supports special memory in which IO data can be |
| * accessed more efficiently. This method provides a mechanism to free |
| * that IO buffer memory. If the underlying hardware does not support |
| * such "special" memory, this functions may simply map to kmm_free(). |
| * |
| * Input Parameters: |
| * drvr - The USB host driver instance obtained as a parameter from the |
| * call to the class create() method. |
| * buffer - The address of the allocated buffer memory to be freed. |
| * |
| * 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 int rx65n_usbhost_iofree(struct usbhost_driver_s *drvr, |
| uint8_t *buffer) |
| { |
| DEBUGASSERT(drvr && buffer); |
| |
| #if RX65N_USBHOST_IOBUFFERS > 0 |
| rx65n_usbhhost_freeio(buffer); |
| return OK; |
| #else |
| return -ENOSYS; |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_ctrlin and rx65n_usbhost_ctrlout |
| * |
| * Description: |
| * Description: |
| * Process a IN or OUT request on the control endpoint. These methods |
| * will enqueue the request and wait for it to complete. Only one |
| * transfer may be queued; Neither these methods nor the transfer() method |
| * can be called again until the control transfer functions returns. |
| * |
| * These are blocking methods; these functions will not return until the |
| * control transfer has completed. |
| * |
| * Input Parameters: |
| * drvr - The USB host driver instance obtained as a parameter from the |
| * call to the class create() method. |
| * ep0 - The control endpoint to send/receive the control request. |
| * req - Describes the request to be sent. This request must lie in |
| * memory created by DRVR_ALLOC. |
| * buffer - A buffer used for sending the request and for returning any |
| * responses. This buffer must be large enough to hold the length value |
| * in the request description. buffer must have been allocated using |
| * DRVR_ALLOC. |
| * |
| * NOTE: On an IN transaction, req and buffer may refer to the same |
| * allocated memory. |
| * |
| * Returned Value: |
| * On success, zero (OK) is returned. On a failure, a negated |
| * errno value is returned indicating the nature of the failure |
| * |
| * Assumptions: |
| * - Called from a single thread so no mutual exclusion is required. |
| * - Never called from an interrupt handler. |
| * |
| ****************************************************************************/ |
| |
| static int rx65n_usbhost_ctrlin(struct usbhost_driver_s *drvr, |
| usbhost_ep_t ep0, |
| const struct usb_ctrlreq_s *req, |
| uint8_t *buffer) |
| { |
| struct rx65n_usbhost_s *priv = (struct rx65n_usbhost_s *)drvr; |
| struct rx65n_usbhost_ed_s *ed = (struct rx65n_usbhost_ed_s *)ep0; |
| uint16_t len; |
| int ret; |
| uint8_t req_type; |
| uint8_t req_req; |
| |
| uint8_t *local_buf; |
| local_buf = buffer; |
| |
| DEBUGASSERT(priv != NULL && ed != NULL && req != NULL); |
| |
| uinfo("type:%02x req:%02x value:%02x%02x index:%02x%02x len:%02x%02x\n", |
| req->type, req->req, req->value[1], req->value[0], |
| req->index[1], req->index[0], req->len[1], req->len[0]); |
| |
| /* We must have exclusive access to EP0 and the control list */ |
| |
| len = rx65n_usbhost_getle16(req->len); |
| req_type = req->type; |
| req_req = req->req; |
| |
| if (req_type == (USB_REQ_DIR_IN | USB_REQ_TYPE_CLASS | |
| USB_REQ_RECIPIENT_INTERFACE) && |
| (req_req == USBHID_REQUEST_GETREPORT)) |
| { |
| /* No need to check for this class request */ |
| } |
| else |
| { |
| ed->xfrinfo->tdxfercond = USB_SETUPRD; |
| ret = rx65n_usbhost_ctrltd(priv, ed, GTD_STATUS_DP_SETUP, |
| (uint8_t *)req, USB_SIZEOF_CTRLREQ); |
| |
| if (ret == OK) |
| { |
| if (len) |
| { |
| ed->xfrinfo->tdxfercond = USB_DATARD; |
| ret = rx65n_usbhost_ctrltd(priv, ed, GTD_STATUS_DP_IN, |
| buffer, len); |
| g_usbidx = USB0.USBINDX; |
| } |
| |
| if (ret == OK) |
| { |
| ed->xfrinfo->tdxfercond = USB_STATUSWR; |
| ret = rx65n_usbhost_ctrltd(priv, ed, |
| GTD_STATUS_DP_OUT, NULL, 0); |
| } |
| } |
| } |
| |
| /* If this is Get Report request */ |
| |
| if (req_type == (USB_REQ_DIR_IN | USB_REQ_TYPE_CLASS | |
| USB_REQ_RECIPIENT_INTERFACE) && |
| (req_req == USBHID_REQUEST_GETREPORT)) |
| { |
| nxsem_wait_uninterruptible( |
| &g_rx65n_edlist[kbd_interrupt_in_pipe].wdhsem); |
| |
| *(local_buf + 0) = kbd_report_data [0]; |
| *(local_buf + 1) = kbd_report_data [1]; |
| *(local_buf + 2) = kbd_report_data [2]; |
| *(local_buf + 3) = kbd_report_data [3]; |
| *(local_buf + 4) = kbd_report_data [4]; |
| *(local_buf + 5) = kbd_report_data [5]; |
| *(local_buf + 6) = kbd_report_data [6]; |
| *(local_buf + 7) = kbd_report_data [7]; |
| ret = OK; |
| |
| /* Reset the transferred count so that for the next interrupt */ |
| |
| /* for read does the calculation correctly */ |
| |
| g_rx65n_edlist[kbd_interrupt_in_pipe].xfrinfo->xfrd = 0; |
| usb_cstd_set_buf(kbd_interrupt_in_pipe); /* Set BUF */ |
| } |
| |
| return ret; |
| } |
| |
| static int rx65n_usbhost_ctrlout(struct usbhost_driver_s *drvr, |
| usbhost_ep_t ep0, const struct usb_ctrlreq_s *req, |
| const uint8_t *buffer) |
| { |
| struct rx65n_usbhost_s *priv = (struct rx65n_usbhost_s *)drvr; |
| struct rx65n_usbhost_ed_s *ed = (struct rx65n_usbhost_ed_s *)ep0; |
| uint16_t len; |
| int ret; |
| static int dev_addressed_state = 0; |
| |
| /* Assumption : This control out is called first time for |
| * set address command. Just resetting the bus after the |
| * set address command |
| */ |
| |
| if (dev_addressed_state == 0) |
| { |
| /* Not sure, if the reset is needed or not - at least the FIT code |
| * does not have reset... |
| * so removing it... |
| * usb_hstd_bus_reset(); |
| */ |
| |
| dev_addressed_state = 0xff; |
| } |
| |
| DEBUGASSERT(priv != NULL && ed != NULL && req != NULL); |
| |
| uinfo("type:%02x req:%02x value:%02x%02x index:%02x%02x len:%02x%02x\n", |
| req->type, req->req, req->value[1], req->value[0], |
| req->index[1], req->index[0], req->len[1], req->len[0]); |
| |
| /* We must have exclusive access to EP0 and the control list */ |
| |
| len = rx65n_usbhost_getle16(req->len); |
| ed->xfrinfo->tdxfercond = USB_SETUPWR; |
| ret = rx65n_usbhost_ctrltd(priv, ed, GTD_STATUS_DP_SETUP, |
| (uint8_t *)req, USB_SIZEOF_CTRLREQ); |
| |
| if (ret == OK) |
| { |
| if (len) |
| { |
| ed->xfrinfo->tdxfercond = USB_DATAWR; |
| ret = rx65n_usbhost_ctrltd(priv, ed, GTD_STATUS_DP_OUT, |
| (uint8_t *)buffer, len); |
| } |
| |
| if (ret == OK) |
| { |
| ed->xfrinfo->tdxfercond = USB_STATUSRD; |
| ret = rx65n_usbhost_ctrltd(priv, ed, GTD_STATUS_DP_IN, NULL, 0); |
| } |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_transfer_common |
| * |
| * Description: |
| * Initiate a request to handle a transfer descriptor. This method will |
| * enqueue the transfer request and return immediately |
| * |
| * Input Parameters: |
| * priv - Internal driver state structure. |
| * ed - The IN or OUT endpoint descriptor for the device endpoint on |
| * which to perform the transfer. |
| * buffer - A buffer containing the data to be sent (OUT endpoint) or |
| * received (IN endpoint). buffer must have been allocated using |
| * DRVR_ALLOC |
| * buflen - The length of the data to be sent or received. |
| * |
| * Returned Value: |
| * On success, zero (OK) is returned. On a failure, a negated errno |
| * value is returned indicating the nature of the failure. |
| * |
| * |
| * Assumptions: |
| * - Called from a single thread so no mutual exclusion is required. |
| * - Never called from an interrupt handler. |
| * |
| ****************************************************************************/ |
| |
| static int rx65n_usbhost_transfer_common(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed, |
| uint8_t *buffer, size_t buflen) |
| { |
| struct rx65n_usbhost_xfrinfo_s *xfrinfo; |
| uint32_t dirpid; |
| bool in; |
| int ret; |
| |
| xfrinfo = ed->xfrinfo; |
| in = (ed->hw.ctrl & ED_CONTROL_D_MASK) == ED_CONTROL_D_IN; |
| |
| uinfo("EP%u %s toggle:%u maxpacket:%u buflen:%lu\n", |
| (ed->hw.ctrl & ED_CONTROL_EN_MASK) >> ED_CONTROL_EN_SHIFT, |
| in ? "IN" : "OUT", |
| (ed->hw.headp & ED_HEADP_C) != 0 ? 1 : 0, |
| (ed->hw.ctrl & ED_CONTROL_MPS_MASK) >> ED_CONTROL_MPS_SHIFT, |
| (unsigned long)buflen); |
| |
| /* Get the direction of the endpoint */ |
| |
| if (in) |
| { |
| dirpid = GTD_STATUS_DP_IN; |
| } |
| else |
| { |
| dirpid = GTD_STATUS_DP_OUT; |
| } |
| |
| /* Then enqueue the transfer */ |
| |
| xfrinfo->tdstatus = TD_CC_NOERROR; |
| |
| ret = rx65n_usbhost_enqueuetd(priv, ed, dirpid, GTD_STATUS_T_TOGGLE, |
| buffer, buflen); |
| |
| if (ed->pipenum == USB_PIPE6 && in == 1) |
| { |
| usb_hstd_receive_start(buffer, buflen, ed->pipenum); |
| } |
| |
| if (ret == OK) |
| { |
| /* BulkListFilled. This bit is used to indicate whether there are any |
| * TDs on the Bulk list. |
| */ |
| |
| if (ed->xfrtype == USB_EP_ATTR_XFER_BULK) |
| { |
| if (in) |
| { |
| usb_hstd_receive_start(buffer, buflen, ed->pipenum); |
| } |
| else |
| { |
| usb_hstd_send_start(buffer, buflen, ed->pipenum); |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_dma_alloc |
| * |
| * Description: |
| * Allocate DMA memory to perform a transfer, copying user data as |
| * necessary |
| * Input Parameters: |
| * priv - Internal driver state structure. |
| * ed - The IN or OUT endpoint descriptor for the device endpoint on which |
| * to perform the transfer. |
| * userbuffer - The user buffer containing the data to be sent (OUT |
| * endpoint) or received (IN endpoint). |
| * buflen - The length of the data to be sent or received. |
| * alloc - The location to return the allocated DMA buffer. |
| * |
| * Returned Value: |
| * On success, zero (OK) is returned. On a failure, a negated errno value |
| * is returned indicating the nature of the failure. |
| * |
| * Assumptions: |
| * - Called from a single thread so no mutual exclusion is required. |
| * - Never called from an interrupt handler. |
| * |
| ****************************************************************************/ |
| |
| #if RX65N_USBHOST_IOBUFFERS > 0 |
| static int rx65n_usbhost_dma_alloc(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed, |
| uint8_t *userbuffer, size_t buflen, |
| uint8_t **alloc) |
| { |
| syslog(LOG_INFO, "Debug : %s(): Line : %d\n", __func__, __LINE__); |
| |
| /* This need to be impemented if DMA is used */ |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_dma_free |
| * |
| * Description: |
| * Free allocated DMA memory. |
| * |
| * Input Parameters: |
| * priv - Internal driver state structure. |
| * ed - The IN or OUT endpoint descriptor for the device endpoint |
| * on which to perform the transfer. |
| * userbuffer - The user buffer containing the data to be sent |
| * (OUT endpoint) or received (IN endpoint). |
| * buflen - The length of the data to be sent or received. |
| * alloc - The allocated DMA buffer to be freed. |
| * |
| * Returned Value: |
| * On success, zero (OK) is returned. On a failure, a negated errno |
| * value is returned indicating the nature of the failure. |
| * |
| * Assumptions: |
| * - Called from a single thread so no mutual exclusion is required. |
| * - Never called from an interrupt handler. |
| * |
| ****************************************************************************/ |
| |
| static void rx65n_usbhost_dma_free(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed, |
| uint8_t *userbuffer, size_t buflen, |
| uint8_t *newbuffer) |
| { |
| syslog(LOG_INFO, "Debug : %s(): Line : %d\n", __func__, __LINE__); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_transfer |
| * |
| * Description: |
| * Process a request to handle a transfer descriptor. This method will |
| * enqueue the transfer request, blocking until the transfer completes. |
| * Only one transfer may be queued; Neither this method nor the ctrlin or |
| * ctrlout methods can be called again until this function returns. |
| * |
| * This is a blocking method; this functions will not return until the |
| * transfer has completed. |
| * |
| * Input Parameters: |
| * drvr - The USB host driver instance obtained as a parameter from the |
| * call to the class create() method. |
| * ep - The IN or OUT endpoint descriptor for the device endpoint on |
| * which to perform the transfer. |
| * buffer - A buffer containing the data to be sent (OUT endpoint) or |
| * received (IN endpoint). buffer must have been allocated using |
| * DRVR_ALLOC |
| * buflen - The length of the data to be sent or received. |
| * |
| * Returned Value: |
| * On success, a non-negative value is returned that indicates the number |
| * of bytes successfully transferred. On a failure, a negated errno |
| * value is returned that indicates the nature of the failure: |
| * |
| * EAGAIN - If devices NAKs the transfer (or NYET or other error where |
| * it may be appropriate to restart the entire transaction). |
| * EPERM - If the endpoint stalls |
| * EIO - On a TX or data toggle error |
| * EPIPE - Overrun errors |
| * |
| * Assumptions: |
| * - Called from a single thread so no mutual exclusion is required. |
| * - Never called from an interrupt handler. |
| * |
| ****************************************************************************/ |
| |
| static ssize_t rx65n_usbhost_transfer(struct usbhost_driver_s *drvr, |
| usbhost_ep_t ep, |
| uint8_t *buffer, size_t buflen) |
| { |
| struct rx65n_usbhost_s *priv = (struct rx65n_usbhost_s *)drvr; |
| struct rx65n_usbhost_ed_s *ed = (struct rx65n_usbhost_ed_s *)ep; |
| struct rx65n_usbhost_xfrinfo_s *xfrinfo; |
| #if RX65N_USBHOST_IOBUFFERS > 0 |
| uint8_t *alloc = NULL; |
| uint8_t *userbuffer = NULL; |
| #endif |
| ssize_t nbytes; |
| int ret; |
| uint8_t transfer_retry_count = 0; |
| |
| DEBUGASSERT(priv && ed && buffer && buflen > 0); |
| |
| if (nrdy_retries[ed->pipenum] != 0) |
| { |
| /* nRdy has occured alreday - just return with -ve value, |
| * so that file close is also completes with this error |
| * |
| */ |
| |
| return -EBUSY; |
| } |
| |
| do |
| { |
| /* We must have exclusive access to the endpoint, the TD pool, the I/O |
| * buffer pool, the bulk and interrupt lists, and the HCCA interrupt |
| * table. |
| * |
| */ |
| |
| nxmutex_lock(&priv->lock); |
| |
| /* Allocate a structure to retain the information needed when the |
| * transfer completes. |
| * |
| */ |
| |
| DEBUGASSERT(ed->xfrinfo == NULL); |
| |
| xfrinfo = rx65n_usbhost_alloc_xfrinfo(); |
| if (xfrinfo == NULL) |
| { |
| uerr("ERROR: rx65n_usbhost_alloc_xfrinfo failed\n"); |
| nbytes = -ENOMEM; |
| nxmutex_unlock(&priv->lock); |
| goto errout_with_lock; |
| } |
| |
| /* Newly added condition */ |
| |
| if (xfrinfo < 0) |
| { |
| uerr("ERROR: rx65n_usbhost_alloc_xfrinfo failed\n"); |
| nbytes = -ENOMEM; |
| goto errout_with_xfrinfo; |
| } |
| |
| /* Initialize the transfer structure */ |
| |
| memset(xfrinfo, 0, sizeof(struct rx65n_usbhost_xfrinfo_s)); |
| xfrinfo->buffer = buffer; |
| xfrinfo->buflen = buflen; |
| |
| /* To begin with transferred bytes = 0 */ |
| |
| xfrinfo->xfrd = 0; |
| |
| ed->xfrinfo = xfrinfo; |
| |
| #if RX65N_USBHOST_IOBUFFERS > 0 |
| /* Allocate an IO buffer if the user buffer does not lie in AHB SRAM */ |
| |
| ret = rx65n_usbhost_dma_alloc(priv, ed, buffer, buflen, &alloc); |
| if (ret < 0) |
| { |
| uerr("ERROR: rx65n_usbhost_dma_alloc failed: %d\n", ret); |
| nbytes = (ssize_t)ret; |
| goto errout_with_xfrinfo; |
| } |
| |
| /* If a buffer was allocated, then use it instead of the |
| * callers buffer |
| * |
| */ |
| |
| if (alloc) |
| { |
| userbuffer = buffer; |
| buffer = alloc; |
| } |
| #endif |
| |
| /* Set the request for the Writeback Done Head event well |
| * BEFORE enabling |
| * the transfer. |
| */ |
| |
| ret = rx65n_usbhost_wdhwait(priv, ed); |
| if (ret < 0) |
| { |
| uerr("ERROR: Device disconnected\n"); |
| nbytes = (ssize_t)ret; |
| goto errout_with_buffers; |
| } |
| |
| /* Set up the transfer */ |
| |
| ret = rx65n_usbhost_transfer_common(priv, ed, buffer, buflen); |
| nxmutex_unlock(&priv->lock); |
| if (ret < 0) |
| { |
| uerr("ERROR: rx65n_usbhost_transfer_common failed: %d\n", ret); |
| nbytes = (ssize_t)ret; |
| goto errout_with_wdhwait; |
| } |
| |
| /* Wait for the Write-back Done Head interrupt */ |
| |
| if (priv->connected) |
| { |
| nxsem_wait_uninterruptible(&ed->wdhsem); |
| } |
| |
| /* Update the buffer pointer for next buffer operation */ |
| |
| /* Check the TD completion status bits */ |
| |
| if (xfrinfo->tdstatus == TD_CC_NOERROR) |
| { |
| /* Return the number of bytes successfully transferred */ |
| |
| nbytes = xfrinfo->xfrd; |
| DEBUGASSERT(nbytes >= 0 && nbytes <= buflen); |
| } |
| else |
| { |
| /* Map the bad completion status to something that a class driver |
| * might understand. |
| */ |
| |
| uerr("ERROR: Bad TD completion status: %d\n", xfrinfo->tdstatus); |
| |
| switch (xfrinfo->tdstatus) |
| { |
| case TD_CC_STALL: |
| nbytes = -EPERM; |
| break; |
| |
| /* This case is newly added for NRDY issue */ |
| |
| case TD_CC_DEVNOTRESPONDING: |
| xfrinfo->wdhwait = false; |
| transfer_retry_count++; |
| nbytes = -EBUSY; |
| break; |
| |
| case TD_CC_USER: |
| nbytes = -ESHUTDOWN; |
| break; |
| |
| default: |
| nbytes = -EIO; |
| break; |
| } |
| } |
| |
| errout_with_wdhwait: |
| |
| /* Make sure that there is no outstanding request on this endpoint */ |
| |
| xfrinfo->wdhwait = false; |
| |
| errout_with_buffers: |
| #if RX65N_USBHOST_IOBUFFERS > 0 |
| |
| /* Free any temporary IO buffers */ |
| |
| rx65n_usbhost_dma_free(priv, ed, userbuffer, buflen, alloc); |
| #endif |
| |
| errout_with_xfrinfo: |
| |
| /* Make sure that there is no outstanding request on this endpoint */ |
| |
| rx65n_usbhost_free_xfrinfo(xfrinfo); |
| ed->xfrinfo = NULL; |
| } |
| while (0); |
| |
| errout_with_lock: |
| |
| /* nxmutex_unlock(&priv->lock); */ |
| |
| return nbytes; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_asynch_completion |
| * |
| * Description: |
| * This function is called at the interrupt level when an asynchronous |
| * transfer completes. It performs the pending callback. |
| * |
| * Input Parameters: |
| * priv - Internal driver state structure. |
| * ep - The IN or OUT endpoint descriptor for the device endpoint on which |
| * the transfer was performed. |
| * |
| * Returned Value: |
| * None |
| * |
| * Assumptions: |
| * - Called from the interrupt level |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_USBHOST_ASYNCH |
| static void rx65n_usbhost_asynch_completion(struct rx65n_usbhost_s *priv, |
| struct rx65n_usbhost_ed_s *ed) |
| { |
| struct rx65n_usbhost_xfrinfo_s *xfrinfo; |
| usbhost_asynch_t callback; |
| void *arg; |
| ssize_t nbytes; |
| |
| DEBUGASSERT(ed != NULL && ed->xfrinfo != NULL); |
| xfrinfo = ed->xfrinfo; |
| |
| DEBUGASSERT(xfrinfo->wdhwait == false && xfrinfo->callback != NULL && |
| xfrinfo->buffer != NULL && xfrinfo->buflen > 0); |
| |
| /* Check the TD completion status bits */ |
| |
| if (xfrinfo->tdstatus == TD_CC_NOERROR) |
| { |
| /* Provide the number of bytes successfully transferred */ |
| |
| nbytes = xfrinfo->xfrd; |
| } |
| else |
| { |
| /* Map the bad completion status to something that a class driver |
| * might understand. |
| */ |
| |
| uerr("ERROR: Bad TD completion status: %d\n", xfrinfo->tdstatus); |
| |
| switch (xfrinfo->tdstatus) |
| { |
| case TD_CC_STALL: |
| nbytes = -EPERM; |
| break; |
| |
| case TD_CC_USER: |
| nbytes = -ESHUTDOWN; |
| break; |
| |
| default: |
| nbytes = -EIO; |
| break; |
| } |
| } |
| |
| #if RX65N_USBHOST_IOBUFFERS > 0 |
| |
| /* Free any temporary IO buffers */ |
| |
| rx65n_usbhost_dma_free(priv, ed, xfrinfo->buffer, xfrinfo->buflen, |
| xfrinfo->alloc); |
| #endif |
| |
| /* Extract the callback information before freeing the buffer */ |
| |
| callback = xfrinfo->callback; |
| arg = xfrinfo->arg; |
| |
| /* Make sure that there is no outstanding request on this endpoint */ |
| |
| rx65n_usbhost_free_xfrinfo(xfrinfo); |
| ed->xfrinfo = NULL; |
| |
| /* Then perform the callback */ |
| |
| callback(arg, nbytes); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_asynch |
| * |
| * Description: |
| * Process a request to handle a transfer descriptor. This method will |
| * enqueue the transfer request and return immediately. When the transfer |
| * completes, the callback will be invoked with the provided transfer. |
| * This method is useful for receiving interrupt transfers which may come |
| * infrequently. |
| * |
| * Only one transfer may be queued; Neither this method nor the ctrlin or |
| * ctrlout methods can be called again until the transfer completes. |
| * |
| * Input Parameters: |
| * drvr - The USB host driver instance obtained as a parameter from the |
| * call to the class create() method. |
| * ep - The IN or OUT endpoint descriptor for the device endpoint on which |
| * to perform the transfer. |
| * buffer - A buffer containing the data to be sent (OUT endpoint) or |
| * received (IN endpoint). buffer must have been allocated using |
| * DRVR_ALLOC |
| * buflen - The length of the data to be sent or received. |
| * callback - This function will be called when the transfer completes. |
| * arg - The arbitrary parameter that will be passed to the callback |
| * function when the transfer completes. |
| * |
| * Returned Value: |
| * On success, zero (OK) is returned. On a failure, a negated errno value |
| * returned indicating the nature of the failure |
| * |
| * Assumptions: |
| * - Called from a single thread so no mutual exclusion is required. |
| * - Never called from an interrupt handler. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_USBHOST_ASYNCH |
| static int rx65n_usbhost_asynch(struct usbhost_driver_s *drvr, |
| usbhost_ep_t ep, |
| uint8_t *buffer, size_t buflen, |
| usbhost_asynch_t callback, void *arg) |
| { |
| struct rx65n_usbhost_s *priv = (struct rx65n_usbhost_s *)drvr; |
| struct rx65n_usbhost_ed_s *ed = (struct rx65n_usbhost_ed_s *)ep; |
| struct rx65n_usbhost_xfrinfo_s *xfrinfo; |
| int ret; |
| |
| DEBUGASSERT(priv && ed && ed->xfrinfo == NULL && |
| buffer && buflen > 0 && callback); |
| |
| /* We must have exclusive access to the endpoint, the TD pool, the I/O |
| * buffer pool, the bulk and interrupt lists, and the HCCA interrupt table. |
| */ |
| |
| nxmutex_lock(&priv->lock); |
| |
| /* Allocate a structure to retain the information needed when the |
| * asynchronous transfer completes. |
| */ |
| |
| DEBUGASSERT(ed->xfrinfo == NULL); |
| |
| xfrinfo = rx65n_usbhost_alloc_xfrinfo(); |
| if (xfrinfo == NULL) |
| { |
| uerr("ERROR: rx65n_usbhost_alloc_xfrinfo failed\n"); |
| ret = -ENOMEM; |
| goto errout_with_lock; |
| } |
| |
| /* Initialize the transfer structure */ |
| |
| memset(xfrinfo, 0, sizeof(struct rx65n_usbhost_xfrinfo_s)); |
| xfrinfo->buffer = buffer; |
| xfrinfo->buflen = buflen; |
| xfrinfo->callback = callback; |
| xfrinfo->arg = arg; |
| |
| ed->xfrinfo = xfrinfo; |
| |
| #if RX65N_USBHOST_IOBUFFERS > 0 |
| /* Allocate an IO buffer if the user buffer does not lie in AHB SRAM */ |
| |
| ret = rx65n_usbhost_dma_alloc(priv, ed, buffer, buflen, &xfrinfo->alloc); |
| if (ret < 0) |
| { |
| uerr("ERROR: rx65n_usbhost_dma_alloc failed: %d\n", ret); |
| goto errout_with_lock; |
| } |
| |
| /* If a buffer was allocated, then use it instead of the callers buffer */ |
| |
| if (xfrinfo->alloc) |
| { |
| buffer = xfrinfo->alloc; |
| } |
| #endif |
| |
| /* Set up the transfer */ |
| |
| ret = rx65n_usbhost_transfer_common(priv, ed, buffer, buflen); |
| if (ret < 0) |
| { |
| uerr("ERROR: rx65n_usbhost_transfer_common failed: %d\n", ret); |
| goto errout_with_asynch; |
| } |
| |
| /* And return now. The callback will be invoked when the transfer |
| * completes. |
| */ |
| |
| /* Enable Ready Interrupt */ |
| |
| nxmutex_unlock(&priv->lock); |
| return OK; |
| |
| errout_with_asynch: |
| #if RX65N_USBHOST_IOBUFFERS > 0 |
| |
| /* Free any temporary IO buffers */ |
| |
| rx65n_usbhost_dma_free(priv, ed, buffer, buflen, xfrinfo->alloc); |
| #endif |
| |
| /* Free the transfer structure */ |
| |
| rx65n_usbhost_free_xfrinfo(xfrinfo); |
| ed->xfrinfo = NULL; |
| |
| errout_with_lock: |
| nxmutex_unlock(&priv->lock); |
| return ret; |
| } |
| #endif /* CONFIG_USBHOST_ASYNCH */ |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_cancel |
| * |
| * Description: |
| * Cancel a pending transfer on an endpoint. Cancelled synchronous or |
| * asynchronous transfer will complete normally with the error -ESHUTDOWN. |
| * |
| * Input Parameters: |
| * drvr - The USB host driver instance obtained as a parameter from the |
| * call to the class create() method. |
| * ep - The IN or OUT endpoint descriptor for the device endpoint on |
| * which an asynchronous transfer should be transferred. |
| * |
| * Returned Value: |
| * On success, zero (OK) is returned. On a failure, a negated errno value |
| * is returned indicating the nature of the failure. |
| * |
| ****************************************************************************/ |
| |
| static int rx65n_usbhost_cancel(struct usbhost_driver_s *drvr, |
| usbhost_ep_t ep) |
| { |
| #ifdef CONFIG_USBHOST_ASYNCH |
| struct rx65n_usbhost_s *priv = (struct rx65n_usbhost_s *)drvr; |
| #endif |
| struct rx65n_usbhost_xfrinfo_s *xfrinfo; |
| irqstate_t flags; |
| |
| /* These first steps must be atomic as possible */ |
| |
| flags = enter_critical_section(); |
| |
| /* It is possible there there is no transfer to be in progress */ |
| |
| xfrinfo = g_rx65n_edlist[USB_PIPE6].xfrinfo; |
| if (xfrinfo) |
| { |
| /* It might be possible for no transfer to be in progress (callback == |
| * NULL and wdhwait == false) |
| */ |
| |
| #ifdef CONFIG_USBHOST_ASYNCH |
| if (xfrinfo->callback || xfrinfo->wdhwait) |
| #else |
| if (xfrinfo->wdhwait) |
| #endif |
| { |
| /* Control endpoints should not come through this path and |
| * isochronous endpoints are not yet implemented. So we only have |
| * to distinguish bulk and interrupt endpoints. |
| */ |
| |
| if (g_rx65n_edlist[USB_PIPE6].xfrtype == USB_EP_ATTR_XFER_BULK) |
| { |
| g_rx65n_edlist[USB_PIPE6].hw.headp = (uint32_t)TDTAIL; |
| g_rx65n_edlist[USB_PIPE6].xfrinfo = NULL; |
| } |
| else |
| { |
| /* Remove the TDs attached to the ED, keeping the Ed in the |
| * list. |
| */ |
| |
| g_rx65n_edlist[USB_PIPE6].hw.headp = (uint32_t)TDTAIL; |
| } |
| |
| xfrinfo->tdstatus = TD_CC_USER; |
| |
| /* If there is a thread waiting for the transfer to complete, then |
| * wake up the thread. |
| */ |
| |
| if (xfrinfo->wdhwait) |
| { |
| #ifdef CONFIG_USBHOST_ASYNCH |
| /* Yes.. there should not also be a callback scheduled */ |
| |
| DEBUGASSERT(xfrinfo->callback == NULL); |
| #endif |
| |
| /* Wake up the waiting thread */ |
| |
| nxsem_post(&g_rx65n_edlist[USB_PIPE6].wdhsem); |
| |
| /* And free the transfer structure */ |
| |
| rx65n_usbhost_free_xfrinfo(xfrinfo); |
| g_rx65n_edlist[USB_PIPE6].xfrinfo = NULL; |
| } |
| #ifdef CONFIG_USBHOST_ASYNCH |
| else |
| { |
| /* Otherwise, perform the callback and free the transfer |
| * structure. |
| */ |
| |
| rx65n_usbhost_asynch_completion(priv, |
| &g_rx65n_edlist[USB_PIPE6]); |
| usb_cstd_clr_pipe_cnfg(USB_PIPE6); |
| g_usb_pipe_table[USB_PIPE6].use_flag = USB_FALSE; |
| } |
| #endif |
| } |
| else |
| { |
| /* Just free the transfer structure */ |
| |
| rx65n_usbhost_free_xfrinfo(xfrinfo); |
| g_rx65n_edlist[USB_PIPE6].xfrinfo = NULL; |
| } |
| } |
| |
| /* Determine the return value */ |
| |
| leave_critical_section(flags); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_connect |
| * |
| * Description: |
| * New connections may be detected by an attached hub. This method is the |
| * mechanism that is used by the hub class to introduce a new connection |
| * and port description to the system. |
| * |
| * Input Parameters: |
| * drvr - The USB host driver instance obtained as a parameter from the |
| * call to the class create() method. |
| * hport - The descriptor of the hub port that detected the connection |
| * related event |
| * connected - True: device connected; false: device disconnected |
| * |
| * Returned Value: |
| * On success, zero (OK) is returned. On a failure, a negated errno |
| * value is returned indicating the nature of the failure. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_USBHOST_HUB |
| static int rx65n_usbhost_connect(struct usbhost_driver_s *drvr, |
| struct usbhost_hubport_s *hport, |
| bool connected) |
| { |
| struct rx65n_usbhost_s *priv = (struct rx65n_usbhost_s *)drvr; |
| DEBUGASSERT(priv != NULL && hport != NULL); |
| |
| int ret; |
| |
| /* Set the connected/disconnected flag */ |
| |
| hport->connected = connected; |
| uinfo("Hub port %d connected: %s\n", hport->port, |
| connected ? "YES" : "NO"); |
| |
| /* Report the connection event */ |
| |
| priv->hport = hport; |
| ret = usbhost_enumerate(hport, &hport->devclass); |
| if (ret < 0) |
| { |
| syslog(LOG_INFO, "Enumeration failed with %d", ret); |
| } |
| |
| hw_usb_write_dcpmxps(USB_DEFPACKET + USB_DEVICE_1); |
| |
| if (hport->speed == USB_SPEED_LOW) |
| { |
| switch (hport->port) |
| { |
| case HUB_PORT1: |
| case HUB_PORT2: |
| case HUB_PORT3: |
| case HUB_PORT4: |
| g_kbdport = hport->port; |
| g_hubkbd = true; |
| break; |
| |
| default: |
| syslog(LOG_INFO, "Undefined Port"); |
| break; |
| } |
| } |
| |
| if (ret >= 0) |
| { |
| syslog(LOG_INFO, "Hub Port device enumerated\n"); |
| } |
| |
| return OK; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_disconnect |
| * |
| * Description: |
| * Called by the class when an error occurs and driver has been |
| * disconnected. The USB host driver should discard the handle to the |
| * class instance (it is stale) and not attempt any further interaction |
| * with the class driver instance (until a new instance is received |
| * from the create() method). |
| * The driver should not called the class' disconnected() method. |
| * |
| * Input Parameters: |
| * drvr - The USB host driver instance obtained as a parameter from the |
| * call to the class create() method. |
| * hport - The port from which the device is being disconnected. |
| * Might be a port on a hub. |
| * |
| * Returned Value: |
| * None |
| * |
| * Assumptions: |
| * - Only a single class bound to a single device is supported. |
| * - Never called from an interrupt handler. |
| * |
| ****************************************************************************/ |
| |
| static void rx65n_usbhost_disconnect(struct usbhost_driver_s *drvr, |
| struct usbhost_hubport_s *hport) |
| { |
| int i; |
| struct rx65n_usbhost_s *priv = &g_usbhost; |
| uint16_t pipe; |
| |
| struct rx65n_usbhost_xfrinfo_s *xfrinfo; |
| uint8_t *buffer; |
| DEBUGASSERT(hport != NULL); |
| |
| if (hport->port) |
| { |
| if (hport->speed == USB_SPEED_LOW) |
| { |
| g_usb_pipe_table[kbd_interrupt_in_pipe].use_flag = USB_FALSE; |
| |
| for (i = 0, xfrinfo = g_xfrbuffers; |
| i < CONFIG_RX65N_USBHOST_NPREALLOC; |
| i++, xfrinfo++) |
| { |
| /* Put the transfer structure in a free list */ |
| #ifdef CONFIG_USBHOST_ASYNCH |
| if (!(xfrinfo->callback)) |
| #endif |
| { |
| rx65n_usbhost_free_xfrinfo(xfrinfo); |
| g_rx65n_edlist[kbd_interrupt_in_pipe].xfrinfo = NULL; |
| } |
| } |
| |
| g_kbdpipe = 0; |
| g_hubkbd = false; |
| syslog(LOG_INFO, "KBD Device Disconnected from Hub\n"); |
| } |
| |
| if (hport->speed == USB_SPEED_FULL) |
| { |
| for (pipe = USB_BULK_PIPE_START ; pipe <= USB_BULK_PIPE_END; |
| pipe++) |
| { |
| if (g_usb_pipe_table[pipe].use_flag == USB_TRUE) |
| { |
| g_usb_pipe_table[pipe].use_flag = USB_FALSE; |
| } |
| } |
| |
| syslog(LOG_INFO, "MSC Device Disconnected from Hub\n"); |
| } |
| } |
| |
| else if (!hport->port) |
| { |
| if (g_usbhost.rhport.hport.speed == USB_SPEED_LOW) |
| { |
| g_usb_pipe_table[kbd_interrupt_in_pipe].use_flag = USB_FALSE; |
| for (i = 0, xfrinfo = g_xfrbuffers; |
| i < CONFIG_RX65N_USBHOST_NPREALLOC; |
| i++, xfrinfo++) |
| { |
| /* Put the transfer structure in a free list */ |
| |
| rx65n_usbhost_free_xfrinfo(xfrinfo); |
| g_rx65n_edlist[kbd_interrupt_in_pipe].xfrinfo = NULL; |
| } |
| } |
| |
| if (g_usbhost.rhport.hport.speed == USB_SPEED_FULL) |
| { |
| if (g_hubkbd) |
| { |
| /* If Keyboard device is connected, and the USB Hub disconnect |
| * task is invoked, then make the Hub task sleep, |
| * so that in this time the keyboard task completes |
| * its disconnection event. |
| * |
| */ |
| |
| nxsig_usleep(100000); |
| } |
| |
| for (i = 0; i < CONFIG_RX65N_USBHOST_NEDS; i++) |
| { |
| /* Put the ED in a free list */ |
| |
| rx65n_usbhost_edfree(&g_rx65n_edlist[i]); |
| } |
| |
| /* Initialize user-configurable TDs */ |
| |
| for (i = 0; i < CONFIG_RX65N_USBHOST_NTDS; i++) |
| { |
| /* Put the TD in a free list */ |
| |
| rx65n_usbhost_tdfree(&g_rx65n_tdlist[i]); |
| } |
| |
| /* Initialize user-configurable request/descriptor transfer |
| * buffers |
| */ |
| |
| buffer = g_tdbuffer; |
| |
| for (i = 0; i < CONFIG_RX65N_USBHOST_TDBUFFERS; i++) |
| { |
| /* Put the TD buffer in a free list */ |
| |
| rx65n_usbhost_tbfree(buffer); |
| buffer += CONFIG_RX65N_USBHOST_TDBUFSIZE; |
| } |
| |
| /* Initialize transfer structures */ |
| |
| for (i = 0, xfrinfo = g_xfrbuffers; |
| i < CONFIG_RX65N_USBHOST_NPREALLOC; |
| i++, xfrinfo++) |
| { |
| /* Put the transfer structure in a free list */ |
| |
| rx65n_usbhost_free_xfrinfo(xfrinfo); |
| } |
| |
| /* Wait 50MS then perform hardware reset */ |
| |
| up_mdelay(50); |
| |
| /* Set up the root hub port EP0 */ |
| |
| rx65n_usbhost_ep0init(priv); |
| |
| /* To begin with make all pipes are available */ |
| |
| for (i = USB_MIN_PIPE_NUM; i < (USB_MAX_PIPE_NUM +1); i++) |
| { |
| g_usb_pipe_table[i].use_flag = USB_FALSE; |
| } |
| } |
| } |
| |
| hport->devclass = NULL; |
| } |
| |
| /**************************************************************************** |
| * Initialization |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_ep0init |
| * |
| * Description: |
| * Initialize ED for EP0, add it to the control ED list, and enable |
| * control transfers. |
| * |
| * Input Parameters: |
| * priv - private driver state instance. |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static inline void rx65n_usbhost_ep0init(struct rx65n_usbhost_s *priv) |
| { |
| /* Initialize the common tail TD. */ |
| |
| memset(TDTAIL, 0, sizeof(struct rx65n_usbhost_gtd_s)); |
| TDTAIL->ed = EDCTRL; |
| |
| /* Link the common tail TD to the ED's TD list */ |
| |
| memset(EDCTRL, 0, sizeof(struct rx65n_usbhost_ed_s)); |
| EDCTRL->hw.headp = (uint32_t)TDTAIL; |
| EDCTRL->hw.tailp = (uint32_t)TDTAIL; |
| EDCTRL->xfrtype = USB_EP_ATTR_XFER_CONTROL; |
| |
| /* Set the head of the control list to the NULL (for now). */ |
| |
| /* Then add EP0 to the empty Control List */ |
| |
| rx65n_usbhost_addctrled(priv, EDCTRL); |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: rx65n_usbhost_initialize |
| * |
| * Description: |
| * Initialize USB host device controller hardware. |
| * |
| * Input Parameters: |
| * controller -- If the device supports more than USB host controller, then |
| * this identifies which controller is being initialized. Normally, this |
| * is just zero. |
| * |
| * Returned Value: |
| * And instance of the USB host interface. The controlling task should |
| * use this interface to (1) call the wait() method to wait for a device |
| * to be connected, and (2) call the enumerate() method to bind the |
| * device to a class driver. |
| * |
| * Assumptions: |
| * - This function should called in the initialization sequence in order |
| * to initialize the USB device functionality. |
| * - Class drivers should be initialized prior to calling this function. |
| * Otherwise, there is a race condition if the device is already . |
| * connected |
| ****************************************************************************/ |
| |
| struct usbhost_connection_s *rx65n_usbhost_initialize(int controller) |
| { |
| struct rx65n_usbhost_s *priv = &g_usbhost; |
| struct usbhost_driver_s *drvr; |
| struct usbhost_hubport_s *hport; |
| struct rx65n_usbhost_xfrinfo_s *xfrinfo; |
| uint32_t reg32; |
| uint8_t *buffer; |
| irqstate_t flags; |
| int i; |
| |
| DEBUGASSERT(controller == 0); |
| DEBUGASSERT(sizeof(struct rx65n_ed_s) <= RX65N_ED_SIZE); |
| DEBUGASSERT(sizeof(struct rx65n_usbhost_gtd_s) <= RX65N_TD_SIZE); |
| |
| /* Initialize all the TDs, EDs (including EDCTRL i.e. |
| * Control Endpoint) and HCCA to 0 |
| */ |
| |
| /* Set HCCA base address */ |
| |
| HCCA = &g_hcca; |
| |
| /* Set TD TAIL address */ |
| |
| TDTAIL = &g_rx65n_tdlist[0]; |
| |
| /* Set ED Tail address as well... */ |
| |
| /* EDCTRL = &g_rx65n_ep0ed; */ |
| |
| /* Set first element as CTRL pipe - as there is always |
| * only one CTRL pipe |
| */ |
| |
| EDCTRL = &g_rx65n_edlist[0]; |
| |
| memset((void *)TDTAIL, 0, |
| ((sizeof(struct ohci_gtd_s)) * (CONFIG_RX65N_USBHOST_NTDS))); |
| memset((void *)(&g_rx65n_edlist[0]), 0, |
| ((sizeof(struct rx65n_usbhost_ed_s)) * |
| (CONFIG_RX65N_USBHOST_NEDS))); |
| memset((void *)EDCTRL, 0, sizeof(struct rx65n_usbhost_ed_s)); |
| memset((void *)HCCA, 0, sizeof(struct ohci_hcca_s)); |
| |
| /* Initialize the state data structure */ |
| |
| /* Initialize the device operations */ |
| |
| drvr = &priv->drvr; |
| drvr->ep0configure = rx65n_usbhost_ep0configure; |
| drvr->epalloc = rx65n_usbhost_epalloc; |
| drvr->epfree = rx65n_usbhost_epfree; |
| drvr->alloc = rx65n_usbhost_alloc; |
| drvr->free = rx65n_usbhost_free; |
| drvr->ioalloc = rx65n_usbhost_ioalloc; |
| drvr->iofree = rx65n_usbhost_iofree; |
| drvr->ctrlin = rx65n_usbhost_ctrlin; |
| drvr->ctrlout = rx65n_usbhost_ctrlout; |
| drvr->transfer = rx65n_usbhost_transfer; |
| #ifdef CONFIG_USBHOST_ASYNCH |
| drvr->asynch = rx65n_usbhost_asynch; |
| #endif |
| drvr->cancel = rx65n_usbhost_cancel; |
| #ifdef CONFIG_USBHOST_HUB |
| drvr->connect = rx65n_usbhost_connect; |
| #endif |
| drvr->disconnect = rx65n_usbhost_disconnect; |
| |
| /* Initialize the public port representation */ |
| |
| hport = &priv->rhport.hport; |
| hport->drvr = drvr; |
| #ifdef CONFIG_USBHOST_HUB |
| hport->parent = NULL; |
| #endif |
| hport->ep0 = EDCTRL; |
| hport->speed = USB_SPEED_FULL; |
| hport->funcaddr = 0; |
| |
| /* Initialize function address generation logic */ |
| |
| usbhost_devaddr_initialize(&priv->devgen); |
| priv->rhport.pdevgen = &priv->devgen; |
| |
| #ifndef CONFIG_USBHOST_INT_DISABLE |
| priv->ininterval = MAX_PERINTERVAL; |
| priv->outinterval = MAX_PERINTERVAL; |
| #endif |
| |
| /* Enable write to System registers */ |
| |
| putreg16(RX65N_PRCR_VALUE, RX65N_PRCR_ADDR); |
| |
| /* Start CMT module */ |
| |
| reg32 = getreg32(RX65N_MSTPCRB_ADDR); |
| |
| /* Clear bit 19 - so that USB module is released from stop state */ |
| |
| reg32 &= (~RX65N_MSTPCRB_START_STOP_USB); |
| putreg32(reg32, RX65N_MSTPCRB_ADDR); |
| |
| reg32 = getreg32(RX65N_MSTPCRB_ADDR); |
| |
| /* Enable power by setting PCUSB in the PCONP register. |
| * Disable interrupts because this register may be shared with other |
| * drivers. |
| */ |
| |
| flags = enter_critical_section(); |
| putreg32(0, RX65N_USB_DPUSR0R); /* FIT code writes 0 to this DPUSR0R */ |
| |
| hw_usb_hmodule_init(); |
| rx65n_usbhost_setbit(RX65N_USB_SOFCFG, RX65N_USB_SOFCFG_TRNENSEL); |
| |
| hw_usb_set_vbout(); |
| up_mdelay(100); |
| |
| leave_critical_section(flags); |
| nxsem_init(&EDCTRL->wdhsem, 0, 0); |
| |
| /* Initialize user-configurable EDs */ |
| |
| for (i = 0; i < CONFIG_RX65N_USBHOST_NEDS; i++) |
| { |
| /* Put the ED in a free list */ |
| |
| rx65n_usbhost_edfree(&g_rx65n_edlist[i]); |
| } |
| |
| /* Initialize user-configurable TDs */ |
| |
| for (i = 0; i < CONFIG_RX65N_USBHOST_NTDS; i++) |
| { |
| /* Put the TD in a free list */ |
| |
| rx65n_usbhost_tdfree(&g_rx65n_tdlist[i]); |
| } |
| |
| /* Initialize user-configurable request/descriptor transfer |
| * buffers |
| */ |
| |
| buffer = g_tdbuffer; |
| |
| for (i = 0; i < CONFIG_RX65N_USBHOST_TDBUFFERS; i++) |
| { |
| /* Put the TD buffer in a free list */ |
| |
| rx65n_usbhost_tbfree(buffer); |
| buffer += CONFIG_RX65N_USBHOST_TDBUFSIZE; |
| } |
| |
| #if RX65N_USBHOST_IOBUFFERS > 0 |
| /* Initialize user-configurable IO buffers */ |
| |
| buffer = (uint8_t *)RX65N_USBHOST_IOFREE_BASE; |
| for (i = 0; i < RX65N_USBHOST_IOBUFFERS; i++) |
| { |
| /* Put the IO buffer in a free list */ |
| |
| rx65n_usbhhost_freeio(buffer); |
| buffer += CONFIG_RX65N_USBHOST_IOBUFSIZE; |
| } |
| #endif |
| |
| /* Initialize transfer structures */ |
| |
| for (i = 0, xfrinfo = g_xfrbuffers; |
| i < CONFIG_RX65N_USBHOST_NPREALLOC; |
| i++, xfrinfo++) |
| { |
| /* Put the transfer structure in a free list */ |
| |
| rx65n_usbhost_free_xfrinfo(xfrinfo); |
| } |
| |
| /* Wait 50MS then perform hardware reset */ |
| |
| up_mdelay(50); |
| |
| /* Set up the root hub port EP0 */ |
| |
| rx65n_usbhost_ep0init(priv); |
| |
| /* To begin with make all pipes are available */ |
| |
| for (i = USB_MIN_PIPE_NUM; i < (USB_MAX_PIPE_NUM +1); i++) |
| { |
| g_usb_pipe_table[i].use_flag = USB_FALSE; |
| } |
| |
| /* Attach USB host controller interrupt handler */ |
| |
| ICU.SLIBR185.BYTE = 0x3eu; |
| IPR(PERIB, INTB185) = _0F_CMTW_PRIORITY_LEVEL15; |
| |
| if (irq_attach(RX65N_INTB185_IRQ, rx65n_usbhost_usbinterrupt, |
| NULL) != 0) |
| { |
| syslog(LOG_INFO, "ERROR: Failed to attach IRQ\n"); |
| return NULL; |
| } |
| |
| IEN(PERIB, INTB185) = 1U; |
| |
| syslog(LOG_INFO, "Debug:USB host Initialized, Device connected:%s\n", |
| priv->connected ? "YES" : "NO"); |
| |
| return &g_usbconn; |
| } |