aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal/src
diff options
context:
space:
mode:
Diffstat (limited to 'os/hal/src')
-rw-r--r--os/hal/src/hal_community.c4
-rw-r--r--os/hal/src/hal_comp.c155
-rw-r--r--os/hal/src/hal_ee24xx.c26
-rw-r--r--os/hal/src/hal_ee25xx.c26
-rw-r--r--os/hal/src/hal_nand.c126
-rw-r--r--os/hal/src/hal_onewire.c15
-rw-r--r--os/hal/src/hal_qei.c163
-rw-r--r--os/hal/src/hal_timcap.c2
-rw-r--r--os/hal/src/hal_usb_hid.c4
-rw-r--r--os/hal/src/hal_usb_msd.c429
-rw-r--r--os/hal/src/hal_usbh.c568
-rw-r--r--os/hal/src/usbh/TODO.txt21
-rw-r--r--os/hal/src/usbh/hal_usbh_aoa.c671
-rw-r--r--os/hal/src/usbh/hal_usbh_debug.c178
-rw-r--r--os/hal/src/usbh/hal_usbh_desciter.c28
-rw-r--r--os/hal/src/usbh/hal_usbh_ftdi.c148
-rw-r--r--os/hal/src/usbh/hal_usbh_hid.c338
-rw-r--r--os/hal/src/usbh/hal_usbh_hub.c65
-rw-r--r--os/hal/src/usbh/hal_usbh_msd.c849
-rw-r--r--os/hal/src/usbh/hal_usbh_uvc.c678
20 files changed, 3497 insertions, 997 deletions
diff --git a/os/hal/src/hal_community.c b/os/hal/src/hal_community.c
index 8a39bf1..ad05fe4 100644
--- a/os/hal/src/hal_community.c
+++ b/os/hal/src/hal_community.c
@@ -80,6 +80,10 @@ void halCommunityInit(void) {
#if HAL_USE_QEI || defined(__DOXYGEN__)
qeiInit();
#endif
+
+#if HAL_USE_COMP || defined(__DOXYGEN__)
+ compInit();
+#endif
}
#endif /* HAL_USE_COMMUNITY */
diff --git a/os/hal/src/hal_comp.c b/os/hal/src/hal_comp.c
new file mode 100644
index 0000000..abc0fad
--- /dev/null
+++ b/os/hal/src/hal_comp.c
@@ -0,0 +1,155 @@
+/*
+ ChibiOS - Copyright (C) 2006..2017 Giovanni Di Sirio
+ Copyright (C) 2017 Fabien Poussin (fabien.poussin (at) google's mail)
+
+ Licensed 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.
+*/
+
+/**
+ * @file hal_comp.c
+ * @brief COMP Driver code.
+ *
+ * @addtogroup COMP
+ * @{
+ */
+
+#include "hal_comp.h"
+
+#if HAL_USE_COMP || defined(__DOXYGEN__)
+
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief COMP Driver initialization.
+ * @note This function is implicitly invoked by @p halInit(), there is
+ * no need to explicitly initialize the driver.
+ *
+ * @init
+ */
+void compInit(void) {
+
+ comp_lld_init();
+}
+
+/**
+ * @brief Initializes the standard part of a @p COMPDriver structure.
+ *
+ * @param[out] compp pointer to the @p COMPDriver object
+ *
+ * @init
+ */
+void compObjectInit(COMPDriver *compp) {
+
+ compp->state = COMP_STOP;
+ compp->config = NULL;
+}
+
+/**
+ * @brief Configures and activates the COMP peripheral.
+ *
+ * @param[in] compp pointer to the @p COMPDriver object
+ * @param[in] config pointer to the @p COMPConfig object
+ *
+ * @api
+ */
+void compStart(COMPDriver *compp, const COMPConfig *config) {
+
+ osalDbgCheck((compp != NULL) && (config != NULL));
+
+ osalSysLock();
+ osalDbgAssert((compp->state == COMP_STOP) || (compp->state == COMP_READY),
+ "invalid state");
+ compp->config = config;
+ comp_lld_start(compp);
+ compp->state = COMP_READY;
+ osalSysUnlock();
+}
+
+/**
+ * @brief Deactivates the COMP peripheral.
+ *
+ * @param[in] compp pointer to the @p COMPDriver object
+ *
+ * @api
+ */
+void compStop(COMPDriver *compp) {
+
+ osalDbgCheck(compp != NULL);
+
+ osalSysLock();
+ osalDbgAssert((compp->state == COMP_STOP) || (compp->state == COMP_READY),
+ "invalid state");
+ comp_lld_stop(compp);
+ compp->state = COMP_STOP;
+ osalSysUnlock();
+}
+
+/**
+ * @brief Activates the comparator.
+ *
+ * @param[in] compp pointer to the @p COMPDriver object
+ *
+ * @api
+ */
+void compEnable(COMPDriver *compp) {
+
+ osalDbgCheck(compp != NULL);
+
+ osalSysLock();
+ osalDbgAssert(compp->state == COMP_READY, "invalid state");
+ comp_lld_enable(compp);
+ compp->state = COMP_ACTIVE;
+ osalSysUnlock();
+}
+
+/**
+ * @brief Deactivates the comparator.
+ *
+ * @param[in] compp pointer to the @p COMPDriver object
+ *
+ * @api
+ */
+void compDisable(COMPDriver *compp) {
+
+ osalDbgCheck(compp != NULL);
+
+ osalSysLock();
+ osalDbgAssert((compp->state == COMP_READY) || (compp->state == COMP_ACTIVE),
+ "invalid state");
+ comp_lld_disable(compp);
+ compp->state = COMP_READY;
+ osalSysUnlock();
+}
+
+#endif /* HAL_USE_COMP */
+
+/** @} */
diff --git a/os/hal/src/hal_ee24xx.c b/os/hal/src/hal_ee24xx.c
index 632ffbb..725258c 100644
--- a/os/hal/src/hal_ee24xx.c
+++ b/os/hal/src/hal_ee24xx.c
@@ -105,7 +105,7 @@ static systime_t calc_timeout(I2CDriver *i2cp, size_t txbytes, size_t rxbytes) {
tmo = ((txbytes + rxbytes + 1) * bitsinbyte * 1000);
tmo /= EEPROM_I2C_CLOCK;
tmo += 10; /* some additional milliseconds to be safer */
- return MS2ST(tmo);
+ return TIME_MS2I(tmo);
}
/**
@@ -202,7 +202,7 @@ static void __fitted_write(void *ip, const uint8_t *data, size_t len, uint32_t *
msg_t status = MSG_RESET;
- osalDbgAssert(len != 0, "something broken in hi level part");
+ osalDbgAssert(len > 0, "len must be greater than 0");
status = eeprom_write(((I2CEepromFileStream *)ip)->cfg,
eepfs_getposition(ip), data, len);
@@ -214,15 +214,15 @@ static void __fitted_write(void *ip, const uint8_t *data, size_t len, uint32_t *
/**
* @brief Write data to EEPROM.
- * @details Only one EEPROM page can be written at once. So fucntion
+ * @details Only one EEPROM page can be written at once. So function
* splits large data chunks in small EEPROM transactions if needed.
- * @note To achieve the maximum effectivity use write operations
+ * @note To achieve the maximum efficiency use write operations
* aligned to EEPROM page boundaries.
*/
static size_t write(void *ip, const uint8_t *bp, size_t n) {
- size_t len = 0; /* bytes to be written at one trasaction */
- uint32_t written; /* total bytes successfully written */
+ size_t len = 0; /* bytes to be written per transaction */
+ uint32_t written = 0; /* total bytes successfully written */
uint16_t pagesize;
uint32_t firstpage;
uint32_t lastpage;
@@ -242,12 +242,10 @@ static size_t write(void *ip, const uint8_t *bp, size_t n) {
lastpage = (((EepromFileStream *)ip)->cfg->barrier_low +
eepfs_getposition(ip) + n - 1) / pagesize;
- written = 0;
- /* data fitted in single page */
+ /* data fits in single page */
if (firstpage == lastpage) {
len = n;
__fitted_write(ip, bp, len, &written);
- bp += len;
return written;
}
@@ -255,17 +253,19 @@ static size_t write(void *ip, const uint8_t *bp, size_t n) {
/* write first piece of data to first page boundary */
len = ((firstpage + 1) * pagesize) - eepfs_getposition(ip);
len -= ((EepromFileStream *)ip)->cfg->barrier_low;
- __fitted_write(ip, bp, len, &written);
+ if (__fitted_write(ip, bp, len, &written) != MSG_OK)
+ return written;
bp += len;
- /* now writes blocks at a size of pages (may be no one) */
+ /* now write page sized blocks (zero or more) */
while ((n - written) > pagesize) {
len = pagesize;
- __fitted_write(ip, bp, len, &written);
+ if (__fitted_write(ip, bp, len, &written) != MSG_OK)
+ return written;
bp += len;
}
- /* wrtie tail */
+ /* write tail */
len = n - written;
if (len == 0)
return written;
diff --git a/os/hal/src/hal_ee25xx.c b/os/hal/src/hal_ee25xx.c
index 102aef8..8c35976 100644
--- a/os/hal/src/hal_ee25xx.c
+++ b/os/hal/src/hal_ee25xx.c
@@ -277,7 +277,7 @@ static msg_t __fitted_write(void *ip, const uint8_t *data, size_t len, uint32_t
msg_t status = MSG_RESET;
- osalDbgAssert(len != 0, "something broken in hi level part");
+ osalDbgAssert(len > 0, "len must be greater than 0");
status = ll_eeprom_write(((SPIEepromFileStream *)ip)->cfg,
eepfs_getposition(ip), data, len);
@@ -290,15 +290,15 @@ static msg_t __fitted_write(void *ip, const uint8_t *data, size_t len, uint32_t
/**
* @brief Write data to EEPROM.
- * @details Only one EEPROM page can be written at once. So fucntion
+ * @details Only one EEPROM page can be written at once. So function
* splits large data chunks in small EEPROM transactions if needed.
- * @note To achieve the maximum effectivity use write operations
+ * @note To achieve the maximum efficiency use write operations
* aligned to EEPROM page boundaries.
*/
static size_t write(void *ip, const uint8_t *bp, size_t n) {
- size_t len = 0; /* bytes to be written at one trasaction */
- uint32_t written; /* total bytes successfully written */
+ size_t len = 0; /* bytes to be written per transaction */
+ uint32_t written = 0; /* total bytes successfully written */
uint16_t pagesize;
uint32_t firstpage;
uint32_t lastpage;
@@ -318,32 +318,30 @@ static size_t write(void *ip, const uint8_t *bp, size_t n) {
firstpage = (cfg->barrier_low + eepfs_getposition(ip)) / pagesize;
lastpage = ((cfg->barrier_low + eepfs_getposition(ip) + n) - 1) / pagesize;
- written = 0;
- /* data fitted in single page */
+ /* data fits in single page */
if (firstpage == lastpage) {
len = n;
__fitted_write(ip, bp, len, &written);
- bp += len;
return written;
}
+
else {
/* write first piece of data to first page boundary */
len = ((firstpage + 1) * pagesize) - eepfs_getposition(ip);
len -= cfg->barrier_low;
- __fitted_write(ip, bp, len, &written);
+ if (__fitted_write(ip, bp, len, &written) != MSG_OK)
+ return written;
bp += len;
- /* now writes blocks at a size of pages (may be no one) */
+ /* now write page sized blocks (zero or more) */
while ((n - written) > pagesize) {
len = pagesize;
- if (__fitted_write(ip, bp, len, &written) != MSG_OK) // Fixed: Would increase bp forever and crash in case of timeouts...
+ if (__fitted_write(ip, bp, len, &written) != MSG_OK)
return written;
-
bp += len;
}
-
- /* wrtie tail */
+ /* write tail */
len = n - written;
if (len == 0)
return written;
diff --git a/os/hal/src/hal_nand.c b/os/hal/src/hal_nand.c
index 24dd6de..a2101d6 100644
--- a/os/hal/src/hal_nand.c
+++ b/os/hal/src/hal_nand.c
@@ -80,16 +80,13 @@ static void pagesize_check(size_t page_data_size) {
*/
static void calc_addr(const NANDConfig *cfg, uint32_t block, uint32_t page,
uint32_t page_offset, uint8_t *addr, size_t addr_len) {
- size_t i = 0;
- uint32_t row = 0;
+ size_t i;
+ uint32_t row;
- /* Incorrect buffer length.*/
osalDbgCheck(cfg->rowcycles + cfg->colcycles == addr_len);
osalDbgCheck((block < cfg->blocks) && (page < cfg->pages_per_block) &&
(page_offset < cfg->page_data_size + cfg->page_spare_size));
- /* convert address to NAND specific */
- memset(addr, 0, addr_len);
row = (block * cfg->pages_per_block) + page;
for (i=0; i<cfg->colcycles; i++){
addr[i] = page_offset & 0xFF;
@@ -115,17 +112,14 @@ static void calc_addr(const NANDConfig *cfg, uint32_t block, uint32_t page,
*/
static void calc_blk_addr(const NANDConfig *cfg, uint32_t block,
uint8_t *addr, size_t addr_len) {
- size_t i = 0;
- uint32_t row = 0;
+ size_t i;
+ uint32_t row;
- /* Incorrect buffer length.*/
- osalDbgCheck(cfg->rowcycles == addr_len);
- osalDbgCheck((block < cfg->blocks));
+ osalDbgCheck(cfg->rowcycles == addr_len); /* Incorrect buffer length */
+ osalDbgCheck(block < cfg->blocks); /* Overflow */
- /* convert address to NAND specific */
- memset(addr, 0, addr_len);
row = block * cfg->pages_per_block;
- for (i=0; i<addr_len; i++){
+ for (i=0; i<addr_len; i++) {
addr[i] = row & 0xFF;
row = row >> 8;
}
@@ -145,12 +139,13 @@ static void calc_blk_addr(const NANDConfig *cfg, uint32_t block,
*/
static bool read_is_block_bad(NANDDriver *nandp, size_t block) {
- if (0xFF != nandReadBadMark(nandp, block, 0))
- return true;
- if (0xFF != nandReadBadMark(nandp, block, 1))
- return true;
+ uint16_t badmark0 = nandReadBadMark(nandp, block, 0);
+ uint16_t badmark1 = nandReadBadMark(nandp, block, 1);
- return false;
+ if ((0xFFFF != badmark0) || (0xFFFF != badmark1))
+ return true;
+ else
+ return false;
}
/**
@@ -235,6 +230,7 @@ void nandStart(NANDDriver *nandp, const NANDConfig *config, bitmap_t *bb_map) {
pagesize_check(nandp->config->page_data_size);
nand_lld_start(nandp);
nandp->state = NAND_READY;
+ nand_lld_reset(nandp);
if (NULL != bb_map) {
nandp->bb_map = bb_map;
@@ -265,24 +261,24 @@ void nandStop(NANDDriver *nandp) {
* @param[in] nandp pointer to the @p NANDDriver object
* @param[in] block block number
* @param[in] page page number related to begin of block
- * @param[out] data buffer to store data
- * @param[in] datalen length of data buffer
+ * @param[out] data buffer to store data, half word aligned
+ * @param[in] datalen length of data buffer in bytes, half word aligned
*
* @api
*/
void nandReadPageWhole(NANDDriver *nandp, uint32_t block, uint32_t page,
- uint8_t *data, size_t datalen) {
+ void *data, size_t datalen) {
const NANDConfig *cfg = nandp->config;
- uint8_t addrbuf[8];
- size_t addrlen = cfg->rowcycles + cfg->colcycles;
+ const size_t addrlen = cfg->rowcycles + cfg->colcycles;
+ uint8_t addr[addrlen];
osalDbgCheck((nandp != NULL) && (data != NULL));
osalDbgCheck((datalen <= (cfg->page_data_size + cfg->page_spare_size)));
osalDbgAssert(nandp->state == NAND_READY, "invalid state");
- calc_addr(cfg, block, page, 0, addrbuf, addrlen);
- nand_lld_read_data(nandp, data, datalen, addrbuf, addrlen, NULL);
+ calc_addr(cfg, block, page, 0, addr, addrlen);
+ nand_lld_read_data(nandp, data, datalen, addr, addrlen, NULL);
}
/**
@@ -291,20 +287,20 @@ void nandReadPageWhole(NANDDriver *nandp, uint32_t block, uint32_t page,
* @param[in] nandp pointer to the @p NANDDriver object
* @param[in] block block number
* @param[in] page page number related to begin of block
- * @param[in] data buffer with data to be written
- * @param[in] datalen length of data buffer
+ * @param[in] data buffer with data to be written, half word aligned
+ * @param[in] datalen length of data buffer in bytes, half word aligned
*
* @return The operation status reported by NAND IC (0x70 command).
*
* @api
*/
uint8_t nandWritePageWhole(NANDDriver *nandp, uint32_t block, uint32_t page,
- const uint8_t *data, size_t datalen) {
+ const void *data, size_t datalen) {
uint8_t retval;
const NANDConfig *cfg = nandp->config;
- uint8_t addr[8];
- size_t addrlen = cfg->rowcycles + cfg->colcycles;
+ const size_t addrlen = cfg->rowcycles + cfg->colcycles;
+ uint8_t addr[addrlen];
osalDbgCheck((nandp != NULL) && (data != NULL));
osalDbgCheck((datalen <= (cfg->page_data_size + cfg->page_spare_size)));
@@ -321,25 +317,25 @@ uint8_t nandWritePageWhole(NANDDriver *nandp, uint32_t block, uint32_t page,
* @param[in] nandp pointer to the @p NANDDriver object
* @param[in] block block number
* @param[in] page page number related to begin of block
- * @param[out] data buffer to store data
- * @param[in] datalen length of data buffer
+ * @param[out] data buffer to store data, half word aligned
+ * @param[in] datalen length of data buffer in bytes, half word aligned
* @param[out] ecc pointer to calculated ECC. Ignored when NULL.
*
* @api
*/
void nandReadPageData(NANDDriver *nandp, uint32_t block, uint32_t page,
- uint8_t *data, size_t datalen, uint32_t *ecc) {
+ void *data, size_t datalen, uint32_t *ecc) {
const NANDConfig *cfg = nandp->config;
- uint8_t addrbuf[8];
- size_t addrlen = cfg->rowcycles + cfg->colcycles;
+ const size_t addrlen = cfg->rowcycles + cfg->colcycles;
+ uint8_t addr[addrlen];
osalDbgCheck((nandp != NULL) && (data != NULL));
osalDbgCheck((datalen <= cfg->page_data_size));
osalDbgAssert(nandp->state == NAND_READY, "invalid state");
- calc_addr(cfg, block, page, 0, addrbuf, addrlen);
- nand_lld_read_data(nandp, data, datalen, addrbuf, addrlen, ecc);
+ calc_addr(cfg, block, page, 0, addr, addrlen);
+ nand_lld_read_data(nandp, data, datalen, addr, addrlen, ecc);
}
/**
@@ -348,8 +344,8 @@ void nandReadPageData(NANDDriver *nandp, uint32_t block, uint32_t page,
* @param[in] nandp pointer to the @p NANDDriver object
* @param[in] block block number
* @param[in] page page number related to begin of block
- * @param[in] data buffer with data to be written
- * @param[in] datalen length of data buffer
+ * @param[in] data buffer with data to be written, half word aligned
+ * @param[in] datalen length of data buffer in bytes, half word aligned
* @param[out] ecc pointer to calculated ECC. Ignored when NULL.
*
* @return The operation status reported by NAND IC (0x70 command).
@@ -357,12 +353,12 @@ void nandReadPageData(NANDDriver *nandp, uint32_t block, uint32_t page,
* @api
*/
uint8_t nandWritePageData(NANDDriver *nandp, uint32_t block, uint32_t page,
- const uint8_t *data, size_t datalen, uint32_t *ecc) {
+ const void *data, size_t datalen, uint32_t *ecc) {
uint8_t retval;
const NANDConfig *cfg = nandp->config;
- uint8_t addr[8];
- size_t addrlen = cfg->rowcycles + cfg->colcycles;
+ const size_t addrlen = cfg->rowcycles + cfg->colcycles;
+ uint8_t addr[addrlen];
osalDbgCheck((nandp != NULL) && (data != NULL));
osalDbgCheck((datalen <= cfg->page_data_size));
@@ -379,17 +375,17 @@ uint8_t nandWritePageData(NANDDriver *nandp, uint32_t block, uint32_t page,
* @param[in] nandp pointer to the @p NANDDriver object
* @param[in] block block number
* @param[in] page page number related to begin of block
- * @param[out] spare buffer to store data
- * @param[in] sparelen length of data buffer
+ * @param[out] spare buffer to store data, half word aligned
+ * @param[in] sparelen length of data buffer in bytes, half word aligned
*
* @api
*/
void nandReadPageSpare(NANDDriver *nandp, uint32_t block, uint32_t page,
- uint8_t *spare, size_t sparelen) {
+ void *spare, size_t sparelen) {
const NANDConfig *cfg = nandp->config;
- uint8_t addr[8];
- size_t addrlen = cfg->rowcycles + cfg->colcycles;
+ const size_t addrlen = cfg->rowcycles + cfg->colcycles;
+ uint8_t addr[addrlen];
osalDbgCheck((NULL != spare) && (nandp != NULL));
osalDbgCheck(sparelen <= cfg->page_spare_size);
@@ -405,28 +401,26 @@ void nandReadPageSpare(NANDDriver *nandp, uint32_t block, uint32_t page,
* @param[in] nandp pointer to the @p NANDDriver object
* @param[in] block block number
* @param[in] page page number related to begin of block
- * @param[in] spare buffer with spare data to be written
- * @param[in] sparelen length of data buffer
+ * @param[in] spare buffer with spare data to be written, half word aligned
+ * @param[in] sparelen length of data buffer in bytes, half word aligned
*
* @return The operation status reported by NAND IC (0x70 command).
*
* @api
*/
uint8_t nandWritePageSpare(NANDDriver *nandp, uint32_t block, uint32_t page,
- const uint8_t *spare, size_t sparelen) {
+ const void *spare, size_t sparelen) {
- uint8_t retVal;
const NANDConfig *cfg = nandp->config;
- uint8_t addr[8];
- size_t addrlen = cfg->rowcycles + cfg->colcycles;
+ const size_t addrlen = cfg->rowcycles + cfg->colcycles;
+ uint8_t addr[addrlen];
osalDbgCheck((NULL != spare) && (nandp != NULL));
osalDbgCheck(sparelen <= cfg->page_spare_size);
osalDbgAssert(nandp->state == NAND_READY, "invalid state");
calc_addr(cfg, block, page, cfg->page_data_size, addr, addrlen);
- retVal = nand_lld_write_data(nandp, spare, sparelen, addr, addrlen, NULL);
- return retVal;
+ return nand_lld_write_data(nandp, spare, sparelen, addr, addrlen, NULL);
}
/**
@@ -439,10 +433,10 @@ uint8_t nandWritePageSpare(NANDDriver *nandp, uint32_t block, uint32_t page,
*/
void nandMarkBad(NANDDriver *nandp, uint32_t block) {
- uint8_t bb_mark[2] = {0, 0};
+ uint16_t bb_mark = 0;
- nandWritePageSpare(nandp, block, 0, bb_mark, sizeof(bb_mark));
- nandWritePageSpare(nandp, block, 1, bb_mark, sizeof(bb_mark));
+ nandWritePageSpare(nandp, block, 0, &bb_mark, sizeof(bb_mark));
+ nandWritePageSpare(nandp, block, 1, &bb_mark, sizeof(bb_mark));
if (NULL != nandp->bb_map)
bitmapSet(nandp->bb_map, block);
@@ -459,11 +453,11 @@ void nandMarkBad(NANDDriver *nandp, uint32_t block) {
*
* @api
*/
-uint8_t nandReadBadMark(NANDDriver *nandp, uint32_t block, uint32_t page) {
- uint8_t bb_mark[1];
+uint16_t nandReadBadMark(NANDDriver *nandp, uint32_t block, uint32_t page) {
+ uint16_t bb_mark;
- nandReadPageSpare(nandp, block, page, bb_mark, sizeof(bb_mark));
- return bb_mark[0];
+ nandReadPageSpare(nandp, block, page, &bb_mark, sizeof(bb_mark));
+ return bb_mark;
}
/**
@@ -478,17 +472,15 @@ uint8_t nandReadBadMark(NANDDriver *nandp, uint32_t block, uint32_t page) {
*/
uint8_t nandErase(NANDDriver *nandp, uint32_t block) {
- uint8_t retVal;
const NANDConfig *cfg = nandp->config;
- uint8_t addr[4];
- size_t addrlen = cfg->rowcycles;
+ const size_t addrlen = cfg->rowcycles;
+ uint8_t addr[addrlen];
osalDbgCheck(nandp != NULL);
osalDbgAssert(nandp->state == NAND_READY, "invalid state");
calc_blk_addr(cfg, block, addr, addrlen);
- retVal = nand_lld_erase(nandp, addr, addrlen);
- return retVal;
+ return nand_lld_erase(nandp, addr, addrlen);
}
/**
diff --git a/os/hal/src/hal_onewire.c b/os/hal/src/hal_onewire.c
index a93eec0..06e63e6 100644
--- a/os/hal/src/hal_onewire.c
+++ b/os/hal/src/hal_onewire.c
@@ -46,7 +46,7 @@ on every timer overflow event.
*/
/**
- * @file onewire.c
+ * @file hal_onewire.c
* @brief 1-wire Driver code.
*
* @addtogroup onewire
@@ -251,7 +251,6 @@ static void ow_write_bit_I(onewireDriver *owp, ioline_t bit) {
static void ow_reset_cb(PWMDriver *pwmp, onewireDriver *owp) {
owp->reg.slave_present = (PAL_LOW == ow_read_bit(owp));
-
osalSysLockFromISR();
pwmDisableChannelI(pwmp, owp->config->sample_channel);
osalThreadResumeI(&owp->thread, MSG_OK);
@@ -661,7 +660,7 @@ bool onewireReset(onewireDriver *owp) {
pwmcfg->channels[mch].callback = NULL;
pwmcfg->channels[mch].mode = owp->config->pwmmode;
pwmcfg->channels[sch].callback = pwm_reset_cb;
- pwmcfg->channels[sch].mode = PWM_OUTPUT_ACTIVE_LOW;
+ pwmcfg->channels[sch].mode = PWM_OUTPUT_DISABLED;
ow_bus_active(owp);
@@ -680,7 +679,7 @@ bool onewireReset(onewireDriver *owp) {
}
/**
- * @brief Read some bites from slave device.
+ * @brief Read some bytes from slave device.
*
* @param[in] owp pointer to the @p onewireDriver object
* @param[out] rxbuf pointer to the buffer for read data
@@ -714,7 +713,7 @@ void onewireRead(onewireDriver *owp, uint8_t *rxbuf, size_t rxbytes) {
pwmcfg->channels[mch].callback = NULL;
pwmcfg->channels[mch].mode = owp->config->pwmmode;
pwmcfg->channels[sch].callback = pwm_read_bit_cb;
- pwmcfg->channels[sch].mode = PWM_OUTPUT_ACTIVE_LOW;
+ pwmcfg->channels[sch].mode = PWM_OUTPUT_DISABLED;
ow_bus_active(owp);
osalSysLock();
@@ -728,7 +727,7 @@ void onewireRead(onewireDriver *owp, uint8_t *rxbuf, size_t rxbytes) {
}
/**
- * @brief Read some bites from slave device.
+ * @brief Write some bytes to slave device.
*
* @param[in] owp pointer to the @p onewireDriver object
* @param[in] txbuf pointer to the buffer with data to be written
@@ -848,7 +847,7 @@ size_t onewireSearchRom(onewireDriver *owp, uint8_t *result,
pwmcfg->channels[mch].callback = NULL;
pwmcfg->channels[mch].mode = owp->config->pwmmode;
pwmcfg->channels[sch].callback = pwm_search_rom_cb;
- pwmcfg->channels[sch].mode = PWM_OUTPUT_ACTIVE_LOW;
+ pwmcfg->channels[sch].mode = PWM_OUTPUT_DISABLED;
ow_bus_active(owp);
osalSysLock();
@@ -882,7 +881,7 @@ size_t onewireSearchRom(onewireDriver *owp, uint8_t *result,
* Include test code (if enabled).
*/
#if ONEWIRE_SYNTH_SEARCH_TEST
-#include "search_rom_synth.c"
+#include "synth_searchrom.c"
#endif
#endif /* HAL_USE_ONEWIRE */
diff --git a/os/hal/src/hal_qei.c b/os/hal/src/hal_qei.c
index a2b7303..9b084f7 100644
--- a/os/hal/src/hal_qei.c
+++ b/os/hal/src/hal_qei.c
@@ -42,6 +42,92 @@
/* Driver local functions. */
/*===========================================================================*/
+/**
+ * @brief Helper for correclty handling overflow/underflow
+ *
+ * @details Underflow/overflow will be handled according to mode:
+ * QEI_OVERFLOW_WRAP: counter value will wrap around.
+ * QEI_OVERFLOW_DISCARD: counter will not change
+ * QEI_OVERFLOW_MINMAX: counter will be updated upto min or max.
+ *
+ * @note This function is for use by low level driver.
+ *
+ * @param[in,out] count counter value
+ * @param[in,out] delta adjustment value
+ * @param[in] min minimum allowed value for counter
+ * @param[in] max maximum allowed value for counter
+ * @param[in] mode how to handle overflow
+ *
+ * @return true if counter underflow/overflow occured or
+ * was due to occur
+ *
+ */
+static inline
+bool qei_adjust_count(qeicnt_t *count, qeidelta_t *delta,
+ qeicnt_t min, qeicnt_t max, qeioverflow_t mode) {
+ /* For information on signed integer overflow see:
+ * https://www.securecoding.cert.org/confluence/x/RgE
+ */
+
+ /* Get values */
+ const qeicnt_t _count = *count;
+ const qeidelta_t _delta = *delta;
+
+ /* Overflow operation
+ */
+ if ((_delta > 0) && (_count > (max - _delta))) {
+ switch(mode) {
+ case QEI_OVERFLOW_WRAP:
+ *delta = 0;
+ *count = (min + (_count - (max - _delta))) - 1;
+ break;
+#if QEI_USE_OVERFLOW_DISCARD == TRUE
+ case QEI_OVERFLOW_DISCARD:
+ *delta = _delta;
+ *count = _count;
+ break;
+#endif
+#if QEI_USE_OVERFLOW_MINMAX == TRUE
+ case QEI_OVERFLOW_MINMAX:
+ *delta = _count - (max - _delta);
+ *count = max;
+ break;
+#endif
+ }
+ return true;
+
+ /* Underflow operation
+ */
+ } else if ((_delta < 0) && (_count < (min - _delta))) {
+ switch(mode) {
+ case QEI_OVERFLOW_WRAP:
+ *delta = 0;
+ *count = (max + (_count - (min - _delta))) + 1;
+ break;
+#if QEI_USE_OVERFLOW_DISCARD == TRUE
+ case QEI_OVERFLOW_DISCARD:
+ *delta = _delta;
+ *count = _count;
+ break;
+#endif
+#if QEI_USE_OVERFLOW_MINMAX == TRUE
+ case QEI_OVERFLOW_MINMAX:
+ *delta = _count - (min - _delta);
+ *count = min;
+ break;
+#endif
+ }
+ return true;
+
+ /* Normal operation
+ */
+ } else {
+ *delta = 0;
+ *count = _count + _delta;
+ return false;
+ }
+}
+
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
@@ -168,6 +254,81 @@ qeicnt_t qeiGetCount(QEIDriver *qeip) {
}
/**
+ * @brief Set counter value.
+ *
+ * @param[in] qeip pointer to the @p QEIDriver object.
+ * @param[in] value the new counter value.
+ *
+ * @api
+ */
+void qeiSetCount(QEIDriver *qeip, qeicnt_t value) {
+ osalDbgCheck(qeip != NULL);
+ osalDbgAssert((qeip->state == QEI_READY) || (qeip->state == QEI_ACTIVE),
+ "invalid state");
+
+ osalSysLock();
+ qei_lld_set_count(qeip, value);
+ osalSysUnlock();
+}
+
+/**
+ * @brief Adjust the counter by delta.
+ *
+ * @param[in] qeip pointer to the @p QEIDriver object.
+ * @param[in] delta the adjustement value.
+ * @return the remaining delta (can occur during overflow).
+ *
+ * @api
+ */
+qeidelta_t qeiAdjust(QEIDriver *qeip, qeidelta_t delta) {
+ osalDbgCheck(qeip != NULL);
+ osalDbgAssert((qeip->state == QEI_ACTIVE), "invalid state");
+
+ osalSysLock();
+ delta = qeiAdjustI(qeip, delta);
+ osalSysUnlock();
+
+ return delta;
+}
+
+/**
+ * @brief Adjust the counter by delta.
+ *
+ * @param[in] qeip pointer to the @p QEIDriver object.
+ * @param[in] delta the adjustement value.
+ * @return the remaining delta (can occur during overflow).
+ *
+ * @api
+ */
+qeidelta_t qeiAdjustI(QEIDriver *qeip, qeidelta_t delta) {
+ /* Get boundaries */
+ qeicnt_t min = QEI_COUNT_MIN;
+ qeicnt_t max = QEI_COUNT_MAX;
+ if (qeip->config->min != qeip->config->max) {
+ min = qeip->config->min;
+ max = qeip->config->max;
+ }
+
+ /* Get counter */
+ qeicnt_t count = qei_lld_get_count(qeip);
+
+ /* Adjust counter value */
+ bool overflowed = qei_adjust_count(&count, &delta,
+ min, max, qeip->config->overflow);
+
+ /* Notify for value change */
+ qei_lld_set_count(qeip, count);
+
+ /* Notify for overflow (passing the remaining delta) */
+ if (overflowed && qeip->config->overflow_cb)
+ qeip->config->overflow_cb(qeip, delta);
+
+ /* Remaining delta */
+ return delta;
+}
+
+
+/**
* @brief Returns the counter delta from last reading.
*
* @param[in] qeip pointer to the @p QEIDriver object
@@ -203,7 +364,7 @@ qeidelta_t qeiUpdateI(QEIDriver *qeip) {
"invalid state");
cnt = qei_lld_get_count(qeip);
- delta = cnt - qeip->last;
+ delta = (qeicnt_t)(cnt - qeip->last);
qeip->last = cnt;
return delta;
diff --git a/os/hal/src/hal_timcap.c b/os/hal/src/hal_timcap.c
index a352490..309c147 100644
--- a/os/hal/src/hal_timcap.c
+++ b/os/hal/src/hal_timcap.c
@@ -19,7 +19,7 @@
*/
/**
- * @file timcap.c
+ * @file hal_timcap.c
* @brief TIMCAP Driver code.
*
* @addtogroup TIMCAP
diff --git a/os/hal/src/hal_usb_hid.c b/os/hal/src/hal_usb_hid.c
index 56be9b7..d9d671b 100644
--- a/os/hal/src/hal_usb_hid.c
+++ b/os/hal/src/hal_usb_hid.c
@@ -221,10 +221,10 @@ void hidObjectInit(USBHIDDriver *uhdp) {
uhdp->vmt = &vmt;
osalEventObjectInit(&uhdp->event);
uhdp->state = HID_STOP;
- ibqObjectInit(&uhdp->ibqueue, uhdp->ib,
+ ibqObjectInit(&uhdp->ibqueue, true, uhdp->ib,
USB_HID_BUFFERS_SIZE, USB_HID_BUFFERS_NUMBER,
ibnotify, uhdp);
- obqObjectInit(&uhdp->obqueue, uhdp->ob,
+ obqObjectInit(&uhdp->obqueue, true, uhdp->ob,
USB_HID_BUFFERS_SIZE, USB_HID_BUFFERS_NUMBER,
obnotify, uhdp);
}
diff --git a/os/hal/src/hal_usb_msd.c b/os/hal/src/hal_usb_msd.c
new file mode 100644
index 0000000..564bad0
--- /dev/null
+++ b/os/hal/src/hal_usb_msd.c
@@ -0,0 +1,429 @@
+/*
+ ChibiOS/HAL - Copyright (C) 2016 Uladzimir Pylinsky aka barthess
+
+ Licensed 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.
+*/
+
+/**
+ * @file hal_usb_msd.c
+ * @brief USM mass storage device code.
+ *
+ * @addtogroup usb_msd
+ * @{
+ */
+
+#include "hal.h"
+
+#if (HAL_USE_USB_MSD == TRUE) || defined(__DOXYGEN__)
+
+#include <string.h>
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+#define MSD_REQ_RESET 0xFF
+#define MSD_GET_MAX_LUN 0xFE
+
+#define MSD_CBW_SIGNATURE 0x43425355
+#define MSD_CSW_SIGNATURE 0x53425355
+
+#define MSD_THD_PRIO NORMALPRIO
+
+#define CBW_FLAGS_RESERVED_MASK 0b01111111
+#define CBW_LUN_RESERVED_MASK 0b11110000
+#define CBW_CMD_LEN_RESERVED_MASK 0b11000000
+
+#define CSW_STATUS_PASSED 0x00
+#define CSW_STATUS_FAILED 0x01
+#define CSW_STATUS_PHASE_ERROR 0x02
+
+#define MSD_SETUP_WORD(setup, index) (uint16_t)(((uint16_t)setup[index+1] << 8)\
+ | (setup[index] & 0x00FF))
+
+#define MSD_SETUP_VALUE(setup) MSD_SETUP_WORD(setup, 2)
+#define MSD_SETUP_INDEX(setup) MSD_SETUP_WORD(setup, 4)
+#define MSD_SETUP_LENGTH(setup) MSD_SETUP_WORD(setup, 6)
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+/**
+ * @brief USB mass storage driver identifier.
+ */
+USBMassStorageDriver USBMSD1;
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/**
+ * @brief Hardcoded default SCSI inquiry response structure.
+ */
+static const scsi_inquiry_response_t default_scsi_inquiry_response = {
+ 0x00, /* direct access block device */
+ 0x80, /* removable */
+ 0x04, /* SPC-2 */
+ 0x02, /* response data format */
+ 0x20, /* response has 0x20 + 4 bytes */
+ 0x00,
+ 0x00,
+ 0x00,
+ "Chibios",
+ "Mass Storage",
+ {'v',CH_KERNEL_MAJOR+'0','.',CH_KERNEL_MINOR+'0'}
+};
+
+/**
+ * @brief Hardcoded default SCSI unit serial number inquiry response structure.
+ */
+static const scsi_unit_serial_number_inquiry_response_t default_scsi_unit_serial_number_inquiry_response =
+{
+ 0x00,
+ 0x80,
+ 0x00,
+ 0x08,
+ "00000000"
+};
+
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Checks validity of CBW content.
+ * @details The device shall consider the CBW valid when:
+ * • The CBW was received after the device had sent a CSW or after a reset,
+ * • the CBW is 31 (1Fh) bytes in length,
+ * • and the dCBWSignature is equal to 43425355h.
+ *
+ * @param[in] cbw pointer to the @p msd_cbw_t object
+ * @param[in] recvd number of received bytes
+ *
+ * @return Operation status.
+ * @retval true CBW is meaningful.
+ * @retval false CBW is bad.
+ *
+ * @notapi
+ */
+static bool cbw_valid(const msd_cbw_t *cbw, msg_t recvd) {
+ if ((sizeof(msd_cbw_t) != recvd) || (cbw->signature != MSD_CBW_SIGNATURE)) {
+ return false;
+ }
+ else {
+ return true;
+ }
+}
+
+/**
+ * @brief Checks meaningfulness of CBW content.
+ * @details The device shall consider the contents of a valid CBW meaningful when:
+ * • no reserved bits are set,
+ * • the bCBWLUN contains a valid LUN supported by the device,
+ * • and both bCBWCBLength and the content of the CBWCB are in
+ * accordance with bInterfaceSubClass.
+ *
+ * @param[in] cbw pointer to the @p msd_cbw_t object
+ *
+ * @return Operation status.
+ * @retval true CBW is meaningful.
+ * @retval false CBW is bad.
+ *
+ * @notapi
+ */
+static bool cbw_meaningful(const msd_cbw_t *cbw) {
+ if (((cbw->cmd_len & CBW_CMD_LEN_RESERVED_MASK) != 0)
+ || ((cbw->flags & CBW_FLAGS_RESERVED_MASK) != 0)
+ || (cbw->lun != 0)) {
+ return false;
+ }
+ else {
+ return true;
+ }
+}
+
+/**
+ * @brief SCSI transport transmit function.
+ *
+ * @param[in] transport pointer to the @p SCSITransport object
+ * @param[in] data payload
+ * @param[in] len number of bytes to be transmitted
+ *
+ * @return Number of successfully transmitted bytes.
+
+ * @notapi
+ */
+static uint32_t scsi_transport_transmit(const SCSITransport *transport,
+ const uint8_t *data, size_t len) {
+
+ usb_scsi_transport_handler_t *trp = transport->handler;
+ msg_t status = usbTransmit(trp->usbp, trp->ep, data, len);
+ if (MSG_OK == status)
+ return len;
+ else
+ return 0;
+}
+
+/**
+ * @brief SCSI transport receive function.
+ *
+ * @param[in] transport pointer to the @p SCSITransport object
+ * @param[in] data payload
+ * @param[in] len number bytes to be received
+ *
+ * @return Number of successfully received bytes.
+
+ * @notapi
+ */
+static uint32_t scsi_transport_receive(const SCSITransport *transport,
+ uint8_t *data, size_t len) {
+
+ usb_scsi_transport_handler_t *trp = transport->handler;
+ msg_t status = usbReceive(trp->usbp, trp->ep, data, len);
+ if (MSG_RESET != status)
+ return status;
+ else
+ return 0;
+}
+
+/**
+ * @brief Fills and sends CSW message.
+ *
+ * @param[in] msdp pointer to the @p USBMassStorageDriver object
+ * @param[in] status status returned by SCSI layer
+ * @param[in] residue number of residue bytes in case of failed transaction
+ *
+ * @notapi
+ */
+static void send_csw(USBMassStorageDriver *msdp, uint8_t status,
+ uint32_t residue) {
+
+ msdp->csw.signature = MSD_CSW_SIGNATURE;
+ msdp->csw.data_residue = residue;
+ msdp->csw.tag = msdp->cbw.tag;
+ msdp->csw.status = status;
+
+ usbTransmit(msdp->usbp, USB_MSD_DATA_EP, (uint8_t *)&msdp->csw,
+ sizeof(msd_csw_t));
+}
+
+/**
+ * @brief Mass storage worker thread.
+ *
+ * @param[in] arg pointer to the @p USBMassStorageDriver object
+ *
+ * @notapi
+ */
+static THD_FUNCTION(usb_msd_worker, arg) {
+ USBMassStorageDriver *msdp = arg;
+ chRegSetThreadName("usb_msd_worker");
+
+ while(! chThdShouldTerminateX()) {
+ const msg_t status = usbReceive(msdp->usbp, USB_MSD_DATA_EP,
+ (uint8_t *)&msdp->cbw, sizeof(msd_cbw_t));
+ if (MSG_RESET == status) {
+ osalThreadSleepMilliseconds(50);
+ }
+ else if (cbw_valid(&msdp->cbw, status) && cbw_meaningful(&msdp->cbw)) {
+ if (SCSI_SUCCESS == scsiExecCmd(&msdp->scsi_target, msdp->cbw.cmd_data)) {
+ send_csw(msdp, CSW_STATUS_PASSED, 0);
+ }
+ else {
+ send_csw(msdp, CSW_STATUS_FAILED, scsiResidue(&msdp->scsi_target));
+ }
+ }
+ else {
+ ; /* do NOT send CSW here. Incorrect CBW must be silently ignored */
+ }
+ }
+
+ chThdExit(MSG_OK);
+}
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Mass storage specific request hook for USB.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ *
+ * @notapi
+ */
+bool msd_request_hook(USBDriver *usbp) {
+
+ if (((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) &&
+ ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) {
+ /* check that the request is for interface 0.*/
+ if (MSD_SETUP_INDEX(usbp->setup) != 0)
+ return false;
+
+ /* act depending on bRequest = setup[1] */
+ switch(usbp->setup[1]) {
+ case MSD_REQ_RESET:
+ /* check that it is a HOST2DEV request */
+ if (((usbp->setup[0] & USB_RTYPE_DIR_MASK) != USB_RTYPE_DIR_HOST2DEV) ||
+ (MSD_SETUP_LENGTH(usbp->setup) != 0) ||
+ (MSD_SETUP_VALUE(usbp->setup) != 0)) {
+ return false;
+ }
+
+ /*
+ As required by the BOT specification, the Bulk-only mass storage reset request (classspecific
+ request) is implemented. This request is used to reset the mass storage device and
+ its associated interface. This class-specific request should prepare the device for the next
+ CBW from the host.
+ To generate the BOT Mass Storage Reset, the host must send a device request on the
+ default pipe of:
+ • bmRequestType: Class, interface, host to device
+ • bRequest field set to 255 (FFh)
+ • wValue field set to ‘0’
+ • wIndex field set to the interface number
+ • wLength field set to ‘0’
+ */
+ chSysLockFromISR();
+
+ /* release and abandon current transmission */
+ usbStallReceiveI(usbp, 1);
+ usbStallTransmitI(usbp, 1);
+ /* The device shall NAK the status stage of the device request until
+ * the Bulk-Only Mass Storage Reset is complete.
+ * NAK EP1 in and out */
+ usbp->otg->ie[1].DIEPCTL = DIEPCTL_SNAK;
+ usbp->otg->oe[1].DOEPCTL = DOEPCTL_SNAK;
+
+ chSysUnlockFromISR();
+
+ /* response to this request using EP0 */
+ usbSetupTransfer(usbp, 0, 0, NULL);
+ return true;
+
+ case MSD_GET_MAX_LUN:
+ /* check that it is a DEV2HOST request */
+ if (((usbp->setup[0] & USB_RTYPE_DIR_MASK) != USB_RTYPE_DIR_DEV2HOST) ||
+ (MSD_SETUP_LENGTH(usbp->setup) != 1) ||
+ (MSD_SETUP_VALUE(usbp->setup) != 0)) {
+ return false;
+ }
+
+ /* stall to indicate that we don't support LUN */
+ osalSysLockFromISR();
+ usbStallTransmitI(usbp, 0);
+ osalSysUnlockFromISR();
+ return true;
+
+ default:
+ return false;
+ break;
+ }
+ }
+ return false;
+}
+
+/**
+ * @brief Initializes the standard part of a @p USBMassStorageDriver structure.
+ *
+ * @param[out] msdp pointer to the @p USBMassStorageDriver object
+ *
+ * @init
+ */
+void msdObjectInit(USBMassStorageDriver *msdp) {
+
+ memset(msdp, 0x55, sizeof(USBMassStorageDriver));
+ msdp->state = USB_MSD_STOP;
+ msdp->usbp = NULL;
+ msdp->worker = NULL;
+
+ scsiObjectInit(&msdp->scsi_target);
+}
+
+/**
+ * @brief Stops the USB mass storage driver.
+ *
+ * @param[in] msdp pointer to the @p USBMassStorageDriver object
+ *
+ * @api
+ */
+void msdStop(USBMassStorageDriver *msdp) {
+
+ osalDbgCheck(msdp != NULL);
+ osalDbgAssert((msdp->state == USB_MSD_READY), "invalid state");
+
+ chThdTerminate(msdp->worker);
+ chThdWait(msdp->worker);
+
+ scsiStop(&msdp->scsi_target);
+
+ msdp->worker = NULL;
+ msdp->state = USB_MSD_STOP;
+ msdp->usbp = NULL;
+}
+
+/**
+ * @brief Configures and activates the USB mass storage driver.
+ *
+ * @param[in] msdp pointer to the @p USBMassStorageDriver object
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] blkdev pointer to the @p BaseBlockDevice object
+ * @param[in] blkbuf pointer to the working area buffer, must be allocated
+ * by user, must be big enough to store 1 data block
+ * @param[in] inquiry pointer to the SCSI inquiry response structure,
+ * set it to @p NULL to use default hardcoded value.
+ *
+ * @api
+ */
+void msdStart(USBMassStorageDriver *msdp, USBDriver *usbp,
+ BaseBlockDevice *blkdev, uint8_t *blkbuf,
+ const scsi_inquiry_response_t *inquiry,
+ const scsi_unit_serial_number_inquiry_response_t *serialInquiry) {
+
+ osalDbgCheck((msdp != NULL) && (usbp != NULL)
+ && (blkdev != NULL) && (blkbuf != NULL));
+ osalDbgAssert((msdp->state == USB_MSD_STOP), "invalid state");
+
+ msdp->usbp = usbp;
+
+ msdp->usb_scsi_transport_handler.usbp = msdp->usbp;
+ msdp->usb_scsi_transport_handler.ep = USB_MSD_DATA_EP;
+ msdp->scsi_transport.handler = &msdp->usb_scsi_transport_handler;
+ msdp->scsi_transport.transmit = scsi_transport_transmit;
+ msdp->scsi_transport.receive = scsi_transport_receive;
+
+ if (NULL == inquiry) {
+ msdp->scsi_config.inquiry_response = &default_scsi_inquiry_response;
+ }
+ else {
+ msdp->scsi_config.inquiry_response = inquiry;
+ }
+ if (NULL == serialInquiry) {
+ msdp->scsi_config.unit_serial_number_inquiry_response = &default_scsi_unit_serial_number_inquiry_response;
+ }
+ else {
+ msdp->scsi_config.unit_serial_number_inquiry_response = serialInquiry;
+ }
+ msdp->scsi_config.blkbuf = blkbuf;
+ msdp->scsi_config.blkdev = blkdev;
+ msdp->scsi_config.transport = &msdp->scsi_transport;
+
+ scsiStart(&msdp->scsi_target, &msdp->scsi_config);
+
+ msdp->state = USB_MSD_READY;
+ msdp->worker = chThdCreateStatic(msdp->waMSDWorker, sizeof(msdp->waMSDWorker),
+ MSD_THD_PRIO, usb_msd_worker, msdp);
+}
+
+#endif /* HAL_USE_USB_MSD */
+
+/** @} */
diff --git a/os/hal/src/hal_usbh.c b/os/hal/src/hal_usbh.c
index 1caa183..7dff98a 100644
--- a/os/hal/src/hal_usbh.c
+++ b/os/hal/src/hal_usbh.c
@@ -1,6 +1,6 @@
/*
- ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio
- Copyright (C) 2015 Diego Ismirlian, TISA, (dismirlian (at) google's mail)
+ ChibiOS - Copyright (C) 2006..2017 Giovanni Di Sirio
+ Copyright (C) 2015..2017 Diego Ismirlian, (dismirlian (at) google's mail)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -19,8 +19,8 @@
#if HAL_USE_USBH
-#include "usbh/dev/hub.h"
#include "usbh/internal.h"
+#include "usbh/dev/hub.h"
#include <string.h>
#if USBH_DEBUG_ENABLE_TRACE
@@ -55,21 +55,18 @@
#define uerr(f, ...) do {} while(0)
#endif
-#if STM32_USBH_USE_OTG1
-USBHDriver USBHD1;
-#endif
-#if STM32_USBH_USE_OTG2
-USBHDriver USBHD2;
-#endif
-
-
static void _classdriver_process_device(usbh_device_t *dev);
-static bool _classdriver_load(usbh_device_t *dev, uint8_t class,
- uint8_t subclass, uint8_t protocol, uint8_t *descbuff, uint16_t rem);
+static bool _classdriver_load(usbh_device_t *dev, uint8_t *descbuff, uint16_t rem);
+#if HAL_USBH_USE_ADDITIONAL_CLASS_DRIVERS
+#include "usbh_additional_class_drivers.h"
+#ifndef HAL_USBH_ADDITIONAL_CLASS_DRIVERS
+#error "Must define HAL_USBH_ADDITIONAL_CLASS_DRIVERS"
+#endif
+#endif
/*===========================================================================*/
-/* Checks. */
+/* Checks. */
/*===========================================================================*/
static inline void _check_dev(usbh_device_t *dev) {
@@ -92,7 +89,7 @@ static inline void _check_urb(usbh_urb_t *urb) {
}
/*===========================================================================*/
-/* Main driver API. */
+/* Main driver API. */
/*===========================================================================*/
void usbhObjectInit(USBHDriver *usbh) {
@@ -106,16 +103,6 @@ void usbhObjectInit(USBHDriver *usbh) {
#endif
}
-void usbhInit(void) {
-#if HAL_USBH_USE_HUB
- uint8_t i;
- for (i = 0; i < HAL_USBHHUB_MAX_INSTANCES; i++) {
- usbhhubObjectInit(&USBHHUBD[i]);
- }
-#endif
- usbh_lld_init();
-}
-
void usbhStart(USBHDriver *usbh) {
usbDbgInit(usbh);
@@ -124,11 +111,9 @@ void usbhStart(USBHDriver *usbh) {
"invalid state");
usbh_lld_start(usbh);
usbh->status = USBH_STATUS_STARTED;
- osalOsRescheduleS();
osalSysUnlock();
}
-
void usbhStop(USBHDriver *usbh) {
//TODO: implement
(void)usbh;
@@ -143,7 +128,7 @@ void usbhResume(USBHDriver *usbh) {
}
/*===========================================================================*/
-/* Endpoint API. */
+/* Endpoint API. */
/*===========================================================================*/
void usbhEPObjectInit(usbh_ep_t *ep, usbh_device_t *dev, const usbh_endpoint_descriptor_t *desc) {
@@ -181,9 +166,28 @@ static void _ep0_object_init(usbh_device_t *dev, uint16_t wMaxPacketSize) {
usbhEPSetName(&dev->ctrl, "DEV[CTRL]");
}
+bool usbhEPReset(usbh_ep_t *ep) {
+ osalDbgCheck(ep != NULL);
+ osalDbgAssert((ep->status == USBH_EPSTATUS_OPEN) || (ep->status == USBH_EPSTATUS_HALTED), "invalid state");
+ osalDbgAssert(ep->type != USBH_EPTYPE_CTRL, "don't need to reset control endpoint");
+
+ usbh_urbstatus_t ret = usbhControlRequest(ep->device,
+ USBH_REQTYPE_STANDARDOUT(USBH_REQTYPE_RECIP_ENDPOINT),
+ USBH_REQ_CLEAR_FEATURE,
+ 0, ep->address | (ep->in ? 0x80 : 0x00), 0, 0);
+
+ /* TODO: GET_STATUS to see if endpoint is still halted */
+ osalSysLock();
+ if ((ret == USBH_URBSTATUS_OK) && usbh_lld_ep_reset(ep)) {
+ osalSysUnlock();
+ return HAL_SUCCESS;
+ }
+ osalSysUnlock();
+ return HAL_FAILED;
+}
/*===========================================================================*/
-/* URB API. */
+/* URB API. */
/*===========================================================================*/
void usbhURBObjectInit(usbh_urb_t *urb, usbh_ep_t *ep, usbh_completion_cb callback,
@@ -219,6 +223,7 @@ void usbhURBObjectResetI(usbh_urb_t *urb) {
usbh_lld_urb_object_reset(urb);
}
+/* usbhURBSubmitI may require a reschedule if called from a S-locked state */
void usbhURBSubmitI(usbh_urb_t *urb) {
osalDbgCheckClassI();
_check_urb(urb);
@@ -228,11 +233,8 @@ void usbhURBSubmitI(usbh_urb_t *urb) {
_usbh_urb_completeI(urb, USBH_URBSTATUS_STALL);
return;
}
- if (ep->status != USBH_EPSTATUS_OPEN) {
- _usbh_urb_completeI(urb, USBH_URBSTATUS_DISCONNECTED);
- return;
- }
- if (!(usbhDeviceGetPort(ep->device)->status & USBH_PORTSTATUS_ENABLE)) {
+ if ((ep->status != USBH_EPSTATUS_OPEN)
+ || !(usbhDeviceGetPort(ep->device)->status & USBH_PORTSTATUS_ENABLE)) {
_usbh_urb_completeI(urb, USBH_URBSTATUS_DISCONNECTED);
return;
}
@@ -240,28 +242,21 @@ void usbhURBSubmitI(usbh_urb_t *urb) {
usbh_lld_urb_submit(urb);
}
+/* _usbh_urb_abortI may require a reschedule if called from a S-locked state */
bool _usbh_urb_abortI(usbh_urb_t *urb, usbh_urbstatus_t status) {
osalDbgCheckClassI();
_check_urb(urb);
+ osalDbgCheck(urb->status != USBH_URBSTATUS_UNINITIALIZED);
- switch (urb->status) {
-/* case USBH_URBSTATUS_UNINITIALIZED:
- * case USBH_URBSTATUS_INITIALIZED:
- * case USBH_URBSTATUS_ERROR:
- * case USBH_URBSTATUS_TIMEOUT:
- * case USBH_URBSTATUS_CANCELLED:
- * case USBH_URBSTATUS_STALL:
- * case USBH_URBSTATUS_DISCONNECTED:
- * case USBH_URBSTATUS_OK: */
- default:
- /* already finished */
- _usbh_urb_completeI(urb, status);
- return TRUE;
-
-// case USBH_URBSTATUS_QUEUED:
- case USBH_URBSTATUS_PENDING:
+ if (urb->status == USBH_URBSTATUS_PENDING) {
return usbh_lld_urb_abort(urb, status);
}
+
+ /* already finished or never submitted:
+ * USBH_URBSTATUS_INITIALIZED, USBH_URBSTATUS_ERROR, USBH_URBSTATUS_TIMEOUT,
+ * USBH_URBSTATUS_CANCELLED, USBH_URBSTATUS_STALL, USBH_URBSTATUS_DISCONNECTED
+ * USBH_URBSTATUS_OK */
+ return TRUE;
}
void _usbh_urb_abort_and_waitS(usbh_urb_t *urb, usbh_urbstatus_t status) {
@@ -271,13 +266,16 @@ void _usbh_urb_abort_and_waitS(usbh_urb_t *urb, usbh_urbstatus_t status) {
if (_usbh_urb_abortI(urb, status) == FALSE) {
uwarn("URB wasn't aborted immediately, suspend");
osalThreadSuspendS(&urb->abortingThread);
- urb->abortingThread = 0;
+ osalDbgAssert(urb->abortingThread == 0, "maybe we should uncomment the line below");
+ //urb->abortingThread = 0;
} else {
+ /* This call is necessary because _usbh_urb_abortI may require a reschedule */
osalOsRescheduleS();
}
uwarn("URB aborted");
}
+/* usbhURBCancelI may require a reschedule if called from a S-locked state */
bool usbhURBCancelI(usbh_urb_t *urb) {
return _usbh_urb_abortI(urb, USBH_URBSTATUS_CANCELLED);
}
@@ -287,36 +285,14 @@ void usbhURBCancelAndWaitS(usbh_urb_t *urb) {
}
msg_t usbhURBWaitTimeoutS(usbh_urb_t *urb, systime_t timeout) {
- msg_t ret;
-
osalDbgCheckClassS();
_check_urb(urb);
-
- switch (urb->status) {
- case USBH_URBSTATUS_INITIALIZED:
- case USBH_URBSTATUS_PENDING:
-// case USBH_URBSTATUS_QUEUED:
- ret = osalThreadSuspendTimeoutS(&urb->waitingThread, timeout);
- urb->waitingThread = 0;
- break;
-
- case USBH_URBSTATUS_OK:
- ret = MSG_OK;
- osalOsRescheduleS();
- break;
-
-/* case USBH_URBSTATUS_UNINITIALIZED:
- * case USBH_URBSTATUS_ERROR:
- * case USBH_URBSTATUS_TIMEOUT:
- * case USBH_URBSTATUS_CANCELLED:
- * case USBH_URBSTATUS_STALL:
- * case USBH_URBSTATUS_DISCONNECTED: */
- default:
- ret = MSG_RESET;
- osalOsRescheduleS();
- break;
+ if (urb->status == USBH_URBSTATUS_OK) {
+ return MSG_OK;
+ } else if (urb->status != USBH_URBSTATUS_PENDING) {
+ return MSG_RESET;
}
- return ret;
+ return osalThreadSuspendTimeoutS(&urb->waitingThread, timeout);
}
msg_t usbhURBSubmitAndWaitS(usbh_urb_t *urb, systime_t timeout) {
@@ -339,6 +315,7 @@ static inline msg_t _wakeup_message(usbh_urbstatus_t status) {
return MSG_RESET;
}
+/* _usbh_urb_completeI may require a reschedule if called from a S-locked state */
void _usbh_urb_completeI(usbh_urb_t *urb, usbh_urbstatus_t status) {
osalDbgCheckClassI();
_check_urb(urb);
@@ -350,7 +327,7 @@ void _usbh_urb_completeI(usbh_urb_t *urb, usbh_urbstatus_t status) {
}
/*===========================================================================*/
-/* Synchronous API. */
+/* Synchronous API. */
/*===========================================================================*/
usbh_urbstatus_t usbhBulkTransfer(usbh_ep_t *ep,
@@ -382,7 +359,8 @@ usbh_urbstatus_t usbhControlRequestExtended(usbh_device_t *dev,
uint32_t *actual_len,
systime_t timeout) {
- _check_dev(dev);
+ if (!dev) return USBH_URBSTATUS_DISCONNECTED;
+
osalDbgCheck(req != NULL);
usbh_urb_t urb;
@@ -408,41 +386,33 @@ usbh_urbstatus_t usbhControlRequest(usbh_device_t *dev,
uint16_t wLength,
uint8_t *buff) {
- const USBH_DEFINE_BUFFER(usbh_control_request_t, req) = {
+ USBH_DEFINE_BUFFER(const usbh_control_request_t req) = {
bmRequestType,
bRequest,
wValue,
wIndex,
wLength
};
- return usbhControlRequestExtended(dev, &req, buff, NULL, MS2ST(1000));
+ return usbhControlRequestExtended(dev, &req, buff, NULL, HAL_USBH_CONTROL_REQUEST_DEFAULT_TIMEOUT);
}
/*===========================================================================*/
-/* Standard request helpers. */
+/* Standard request helpers. */
/*===========================================================================*/
-#define USBH_GET_DESCRIPTOR(type, value, index) \
- USBH_STANDARDIN(type, \
- USBH_REQ_GET_DESCRIPTOR, \
- value, \
- index) \
-
-#define USBH_GETDEVICEDESCRIPTOR \
- USBH_GET_DESCRIPTOR(USBH_REQTYPE_DEVICE, (USBH_DT_DEVICE << 8) | 0, 0)
-
-#define USBH_GETCONFIGURATIONDESCRIPTOR(index) \
- USBH_GET_DESCRIPTOR(USBH_REQTYPE_DEVICE, (USBH_DT_CONFIG << 8) | index, 0)
-
-#define USBH_GETSTRINGDESCRIPTOR(index, langID) \
- USBH_GET_DESCRIPTOR(USBH_REQTYPE_DEVICE, (USBH_DT_STRING << 8) | index, langID)
-
bool usbhStdReqGetDeviceDescriptor(usbh_device_t *dev,
uint16_t wLength,
uint8_t *buf) {
usbh_device_descriptor_t *desc;
- usbh_urbstatus_t ret = usbhControlRequest(dev, USBH_GETDEVICEDESCRIPTOR, wLength, buf);
+
+ usbh_urbstatus_t ret = usbhControlRequest(dev,
+ USBH_REQTYPE_STANDARDIN(USBH_REQTYPE_RECIP_DEVICE),
+ USBH_REQ_GET_DESCRIPTOR,
+ (USBH_DT_DEVICE << 8) | 0, 0,
+ wLength, buf);
+
desc = (usbh_device_descriptor_t *)buf;
+
if ((ret != USBH_URBSTATUS_OK)
|| (desc->bLength != USBH_DT_DEVICE_SIZE)
|| (desc->bDescriptorType != USBH_DT_DEVICE)) {
@@ -455,8 +425,15 @@ bool usbhStdReqGetConfigurationDescriptor(usbh_device_t *dev,
uint8_t index,
uint16_t wLength,
uint8_t *buf) {
- usbh_urbstatus_t ret = usbhControlRequest(dev, USBH_GETCONFIGURATIONDESCRIPTOR(index), wLength, buf);
+
+ usbh_urbstatus_t ret = usbhControlRequest(dev,
+ USBH_REQTYPE_STANDARDIN(USBH_REQTYPE_RECIP_DEVICE),
+ USBH_REQ_GET_DESCRIPTOR,
+ (USBH_DT_CONFIG << 8) | index, 0,
+ wLength, buf);
+
usbh_config_descriptor_t *const desc = (usbh_config_descriptor_t *)buf;
+
if ((ret != USBH_URBSTATUS_OK)
|| (desc->bLength < USBH_DT_CONFIG_SIZE)
|| (desc->bDescriptorType != USBH_DT_CONFIG)) {
@@ -473,7 +450,13 @@ bool usbhStdReqGetStringDescriptor(usbh_device_t *dev,
osalDbgAssert(wLength >= USBH_DT_STRING_SIZE, "wrong size");
usbh_string_descriptor_t *desc = (usbh_string_descriptor_t *)buf;
- usbh_urbstatus_t ret = usbhControlRequest(dev, USBH_GETSTRINGDESCRIPTOR(index, langID), wLength, buf);
+
+ usbh_urbstatus_t ret = usbhControlRequest(dev,
+ USBH_REQTYPE_STANDARDIN(USBH_REQTYPE_RECIP_DEVICE),
+ USBH_REQ_GET_DESCRIPTOR,
+ (USBH_DT_STRING << 8) | index, langID,
+ wLength, buf);
+
if ((ret != USBH_URBSTATUS_OK)
|| (desc->bLength < USBH_DT_STRING_SIZE)
|| (desc->bDescriptorType != USBH_DT_STRING)) {
@@ -482,25 +465,17 @@ bool usbhStdReqGetStringDescriptor(usbh_device_t *dev,
return HAL_SUCCESS;
}
-
-
-#define USBH_SET_INTERFACE(interface, alt) \
- USBH_STANDARDOUT(USBH_REQTYPE_INTERFACE, \
- USBH_REQ_SET_INTERFACE, \
- alt, \
- interface) \
-
-#define USBH_GET_INTERFACE(interface) \
- USBH_STANDARDIN(USBH_REQTYPE_INTERFACE, \
- USBH_REQ_GET_INTERFACE, \
- 0, \
- interface) \
-
bool usbhStdReqSetInterface(usbh_device_t *dev,
uint8_t bInterfaceNumber,
uint8_t bAlternateSetting) {
- usbh_urbstatus_t ret = usbhControlRequest(dev, USBH_SET_INTERFACE(bInterfaceNumber, bAlternateSetting), 0, NULL);
+ usbh_urbstatus_t ret = usbhControlRequest(dev,
+ USBH_REQTYPE_STANDARDOUT(USBH_REQTYPE_RECIP_INTERFACE),
+ USBH_REQ_SET_INTERFACE,
+ bAlternateSetting,
+ bInterfaceNumber,
+ 0, NULL);
+
if (ret != USBH_URBSTATUS_OK)
return HAL_FAILED;
@@ -511,9 +486,15 @@ bool usbhStdReqGetInterface(usbh_device_t *dev,
uint8_t bInterfaceNumber,
uint8_t *bAlternateSetting) {
- USBH_DEFINE_BUFFER(uint8_t, alt);
+ USBH_DEFINE_BUFFER(uint8_t alt);
+
+ usbh_urbstatus_t ret = usbhControlRequest(dev,
+ USBH_REQTYPE_STANDARDIN(USBH_REQTYPE_RECIP_INTERFACE),
+ USBH_REQ_GET_INTERFACE,
+ 0,
+ bInterfaceNumber,
+ 1, &alt);
- usbh_urbstatus_t ret = usbhControlRequest(dev, USBH_GET_INTERFACE(bInterfaceNumber), 1, &alt);
if (ret != USBH_URBSTATUS_OK)
return HAL_FAILED;
@@ -523,7 +504,7 @@ bool usbhStdReqGetInterface(usbh_device_t *dev,
/*===========================================================================*/
-/* Device-related functions. */
+/* Device-related functions. */
/*===========================================================================*/
static uint8_t _find_address(USBHDriver *host) {
@@ -558,7 +539,8 @@ static void _device_initialize(usbh_device_t *dev, usbh_devspeed_t speed) {
static bool _device_setaddress(usbh_device_t *dev, uint8_t address) {
usbh_urbstatus_t ret = usbhControlRequest(dev,
- USBH_STANDARDOUT(USBH_REQTYPE_DEVICE, USBH_REQ_SET_ADDRESS, address, 0),
+ USBH_REQTYPE_STANDARDOUT(USBH_REQTYPE_RECIP_DEVICE),
+ USBH_REQ_SET_ADDRESS, address, 0,
0,
0);
if (ret != USBH_URBSTATUS_OK)
@@ -611,22 +593,12 @@ static void _device_free_full_cfgdesc(usbh_device_t *dev) {
}
}
-
-#define USBH_SET_CONFIGURATION(type, value, index) \
- USBH_STANDARDOUT(type, \
- USBH_REQ_SET_CONFIGURATION, \
- value, \
- index) \
-
-#define USBH_SETDEVICECONFIGURATION(index) \
- USBH_SET_CONFIGURATION(USBH_REQTYPE_DEVICE, index, 0)
-
-
static bool _device_set_configuration(usbh_device_t *dev, uint8_t configuration) {
usbh_urbstatus_t ret = usbhControlRequest(dev,
- USBH_SETDEVICECONFIGURATION(configuration),
- 0,
- 0);
+ USBH_REQTYPE_STANDARDOUT(USBH_REQTYPE_RECIP_DEVICE),
+ USBH_REQ_SET_CONFIGURATION,
+ configuration,
+ 0, 0, 0);
if (ret != USBH_URBSTATUS_OK)
return HAL_FAILED;
return HAL_SUCCESS;
@@ -720,7 +692,7 @@ static bool _device_enumerate(usbh_device_t *dev) {
#if USBH_DEBUG_ENABLE && USBH_DEBUG_ENABLE_INFO
void usbhDevicePrintInfo(usbh_device_t *dev) {
- USBH_DEFINE_BUFFER(char, str[64]);
+ USBH_DEFINE_BUFFER(char str[64]);
usbh_device_descriptor_t *const desc = &dev->devDesc;
uinfo("----- Device info -----");
@@ -842,11 +814,8 @@ bool usbhDeviceReadString(usbh_device_t *dev, char *dest, uint8_t size,
return HAL_SUCCESS;
}
-
-
-
/*===========================================================================*/
-/* Port processing functions. */
+/* Port processing functions. */
/*===========================================================================*/
static void _port_connected(usbh_port_t *port);
@@ -856,7 +825,7 @@ static void _port_reset(usbh_port_t *port) {
#if HAL_USBH_USE_HUB
port->hub,
#endif
- USBH_REQTYPE_OUT | USBH_REQTYPE_CLASS | USBH_REQTYPE_OTHER,
+ USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_TYPE_CLASS | USBH_REQTYPE_RECIP_OTHER,
USBH_REQ_SET_FEATURE,
USBH_PORT_FEAT_RESET,
port->number,
@@ -870,7 +839,7 @@ static void _port_update_status(usbh_port_t *port) {
#if HAL_USBH_USE_HUB
port->hub,
#endif
- USBH_REQTYPE_IN | USBH_REQTYPE_CLASS | USBH_REQTYPE_OTHER,
+ USBH_REQTYPE_DIR_IN | USBH_REQTYPE_TYPE_CLASS | USBH_REQTYPE_RECIP_OTHER,
USBH_REQ_GET_STATUS,
0,
port->number,
@@ -887,46 +856,48 @@ static void _port_process_status_change(usbh_port_t *port) {
_port_update_status(port);
if (port->c_status & USBH_PORTSTATUS_C_CONNECTION) {
- /* port connected status changed */
port->c_status &= ~USBH_PORTSTATUS_C_CONNECTION;
usbhhubClearFeaturePort(port, USBH_PORT_FEAT_C_CONNECTION);
- if ((port->status & (USBH_PORTSTATUS_CONNECTION | USBH_PORTSTATUS_ENABLE))
- == USBH_PORTSTATUS_CONNECTION) {
- if (port->device.status != USBH_DEVSTATUS_DISCONNECTED) {
+
+ if (port->device.status != USBH_DEVSTATUS_DISCONNECTED) {
+ if (!(port->status & USBH_PORTSTATUS_CONNECTION)) {
_usbh_port_disconnected(port);
}
+ }
+ }
- /* connected, disabled */
+ if (port->device.status == USBH_DEVSTATUS_DISCONNECTED) {
+ if (port->status & USBH_PORTSTATUS_CONNECTION) {
_port_connected(port);
- } else {
- /* disconnected */
- _usbh_port_disconnected(port);
}
}
if (port->c_status & USBH_PORTSTATUS_C_RESET) {
port->c_status &= ~USBH_PORTSTATUS_C_RESET;
usbhhubClearFeaturePort(port, USBH_PORT_FEAT_C_RESET);
+ udbgf("Port %d: reset=%d", port->number, port->status & USBH_PORTSTATUS_RESET ? 1 : 0);
}
if (port->c_status & USBH_PORTSTATUS_C_ENABLE) {
port->c_status &= ~USBH_PORTSTATUS_C_ENABLE;
usbhhubClearFeaturePort(port, USBH_PORT_FEAT_C_ENABLE);
+ udbgf("Port %d: enable=%d", port->number, port->status & USBH_PORTSTATUS_ENABLE ? 1 : 0);
}
if (port->c_status & USBH_PORTSTATUS_C_OVERCURRENT) {
port->c_status &= ~USBH_PORTSTATUS_C_OVERCURRENT;
usbhhubClearFeaturePort(port, USBH_PORT_FEAT_C_OVERCURRENT);
+ uwarnf("Port %d: overcurrent=%d", port->number, port->status & USBH_PORTSTATUS_OVERCURRENT ? 1 : 0);
}
if (port->c_status & USBH_PORTSTATUS_C_SUSPEND) {
port->c_status &= ~USBH_PORTSTATUS_C_SUSPEND;
usbhhubClearFeaturePort(port, USBH_PORT_FEAT_C_SUSPEND);
+ uinfof("Port %d: suspend=%d", port->number, port->status & USBH_PORTSTATUS_SUSPEND ? 1 : 0);
}
}
-
static void _port_connected(usbh_port_t *port) {
/* connected */
@@ -934,11 +905,10 @@ static void _port_connected(usbh_port_t *port) {
uint8_t i;
uint8_t retries;
usbh_devspeed_t speed;
- USBH_DEFINE_BUFFER(usbh_string_descriptor_t, strdesc);
-
- uinfof("Port %d connected, wait debounce...", port->number);
+ USBH_DEFINE_BUFFER(usbh_string_descriptor_t strdesc);
port->device.status = USBH_DEVSTATUS_ATTACHED;
+ uinfof("Port %d: attached, wait debounce...", port->number);
/* wait for attach de-bounce */
osalThreadSleepMilliseconds(HAL_USBH_PORT_DEBOUNCE_TIME);
@@ -946,16 +916,26 @@ static void _port_connected(usbh_port_t *port) {
/* check disconnection */
_port_update_status(port);
if (port->c_status & USBH_PORTSTATUS_C_CONNECTION) {
- /* connection state changed; abort */
+ port->c_status &= ~USBH_PORTSTATUS_C_CONNECTION;
+ usbhhubClearFeaturePort(port, USBH_PORT_FEAT_C_CONNECTION);
+ uwarnf("Port %d: connection state changed; abort #1", port->number);
+ goto abort;
+ }
+
+ /* make sure that the device is still connected */
+ if ((port->status & USBH_PORTSTATUS_CONNECTION) == 0) {
+ uwarnf("Port %d: device is disconnected", port->number);
goto abort;
}
+ uinfof("Port %d: connected", port->number);
port->device.status = USBH_DEVSTATUS_CONNECTED;
retries = 3;
reset:
for (i = 0; i < 3; i++) {
- uinfo("Try reset...");
+ uinfof("Port %d: Try reset...", port->number);
+ /* TODO: check that port is actually disabled */
port->c_status &= ~(USBH_PORTSTATUS_C_RESET | USBH_PORTSTATUS_C_ENABLE);
_port_reset(port);
osalThreadSleepMilliseconds(20); /* give it some time to reset (min. 10ms) */
@@ -964,8 +944,12 @@ reset:
_port_update_status(port);
/* check for disconnection */
- if (port->c_status & USBH_PORTSTATUS_C_CONNECTION)
+ if (port->c_status & USBH_PORTSTATUS_C_CONNECTION) {
+ port->c_status &= ~USBH_PORTSTATUS_C_CONNECTION;
+ usbhhubClearFeaturePort(port, USBH_PORT_FEAT_C_CONNECTION);
+ uwarnf("Port %d: connection state changed; abort #2", port->number);
goto abort;
+ }
/* check for reset completion */
if (port->c_status & USBH_PORTSTATUS_C_RESET) {
@@ -979,7 +963,10 @@ reset:
}
/* check for timeout */
- if (osalOsGetSystemTimeX() - start > HAL_USBH_PORT_RESET_TIMEOUT) break;
+ if (osalOsGetSystemTimeX() - start > HAL_USBH_PORT_RESET_TIMEOUT) {
+ uwarnf("Port %d: reset timeout", port->number);
+ break;
+ }
}
}
@@ -987,8 +974,7 @@ reset:
goto abort;
reset_success:
-
- uinfo("Reset OK, recovery...");
+ uinfof("Port %d: Reset OK, recovery...", port->number);
/* reset recovery */
osalThreadSleepMilliseconds(100);
@@ -1005,19 +991,22 @@ reset_success:
usbhEPOpen(&port->device.ctrl);
/* device with default address (0), try enumeration */
- if (_device_enumerate(&port->device)) {
+ if (_device_enumerate(&port->device) != HAL_SUCCESS) {
/* enumeration failed */
usbhEPClose(&port->device.ctrl);
- if (!--retries)
+ if (!--retries) {
+ uwarnf("Port %d: enumeration failed; abort", port->number);
goto abort;
+ }
/* retry reset & enumeration */
+ uwarnf("Port %d: enumeration failed; retry reset & enumeration", port->number);
goto reset;
}
/* load the default language ID */
- uinfo("Loading langID0...");
+ uinfof("Port %d: Loading langID0...", port->number);
if (!usbhStdReqGetStringDescriptor(&port->device, 0, 0,
USBH_DT_STRING_SIZE, (uint8_t *)&strdesc)
&& (strdesc.bLength >= 4)
@@ -1025,12 +1014,12 @@ reset_success:
4, (uint8_t *)&strdesc)) {
port->device.langID0 = strdesc.wData[0];
- uinfof("langID0=%04x", port->device.langID0);
+ uinfof("Port %d: langID0=%04x", port->number, port->device.langID0);
}
/* check if the device has only one configuration */
if (port->device.devDesc.bNumConfigurations == 1) {
- uinfo("Device has only one configuration");
+ uinfof("Port %d: device has only one configuration", port->number);
_device_configure(&port->device, 0);
}
@@ -1038,7 +1027,7 @@ reset_success:
return;
abort:
- uerr("Abort");
+ uerrf("Port %d: abort", port->number);
port->device.status = USBH_DEVSTATUS_DISCONNECTED;
}
@@ -1046,14 +1035,14 @@ void _usbh_port_disconnected(usbh_port_t *port) {
if (port->device.status == USBH_DEVSTATUS_DISCONNECTED)
return;
- uinfo("Port disconnected");
+ uinfof("Port %d: disconnected", port->number);
/* unload drivers */
while (port->device.drivers) {
usbh_baseclassdriver_t *drv = port->device.drivers;
/* unload */
- uinfof("Unload driver %s", drv->info->name);
+ uinfof("Port %d: unload driver %s", port->number, drv->info->name);
drv->info->vmt->unload(drv);
/* unlink */
@@ -1062,9 +1051,7 @@ void _usbh_port_disconnected(usbh_port_t *port) {
}
/* close control endpoint */
- osalSysLock();
- usbhEPCloseS(&port->device.ctrl);
- osalSysUnlock();
+ usbhEPClose(&port->device.ctrl);
/* free address */
if (port->device.address)
@@ -1076,9 +1063,8 @@ void _usbh_port_disconnected(usbh_port_t *port) {
}
-
/*===========================================================================*/
-/* Hub processing functions. */
+/* Hub processing functions. */
/*===========================================================================*/
#if HAL_USBH_USE_HUB
@@ -1086,7 +1072,7 @@ static void _hub_update_status(USBHDriver *host, USBHHubDriver *hub) {
uint32_t stat;
if (usbhhubControlRequest(host,
hub,
- USBH_REQTYPE_IN | USBH_REQTYPE_CLASS | USBH_REQTYPE_DEVICE,
+ USBH_REQTYPE_DIR_IN | USBH_REQTYPE_TYPE_CLASS | USBH_REQTYPE_RECIP_DEVICE,
USBH_REQ_GET_STATUS,
0,
0,
@@ -1104,7 +1090,7 @@ static void _hub_process_status_change(USBHDriver *host, USBHHubDriver *hub) {
uinfo("Hub status change. GET_STATUS.");
_hub_update_status(host, hub);
- if (hub->c_status & USBH_HUBSTATUS_C_HUB_LOCAL_POWER) {
+ if (hub->c_status & USBH_HUBSTATUS_C_HUB_LOCAL_POWER) {
hub->c_status &= ~USBH_HUBSTATUS_C_HUB_LOCAL_POWER;
uinfo("Clear USBH_HUB_FEAT_C_HUB_LOCAL_POWER");
usbhhubClearFeatureHub(host, hub, USBH_HUB_FEAT_C_HUB_LOCAL_POWER);
@@ -1122,7 +1108,6 @@ static uint32_t _hub_get_status_change_bitmap(USBHDriver *host, USBHHubDriver *h
osalSysLock();
uint32_t ret = hub->statuschange;
hub->statuschange = 0;
- osalOsRescheduleS();
osalSysUnlock();
return ret;
}
@@ -1176,7 +1161,7 @@ static void _hub_process(USBHDriver *host) {
#endif
/*===========================================================================*/
-/* Main processing loop (enumeration, loading/unloading drivers, etc). */
+/* Main processing loop (enumeration, loading/unloading drivers, etc). */
/*===========================================================================*/
void usbhMainLoop(USBHDriver *usbh) {
@@ -1188,8 +1173,8 @@ void usbhMainLoop(USBHDriver *usbh) {
_hub_process(usbh, NULL);
/* process connected hubs */
- USBHHubDriver *hub;
- list_for_each_entry(hub, USBHHubDriver, &usbh->hubs, node) {
+ USBHHubDriver *hub, *temp;
+ list_for_each_entry_safe(hub, USBHHubDriver, temp, &usbh->hubs, node) {
_hub_process(usbh, hub);
}
#else
@@ -1198,75 +1183,78 @@ void usbhMainLoop(USBHDriver *usbh) {
#endif
}
-
/*===========================================================================*/
-/* IAD class driver. */
+/* Class driver loader. */
/*===========================================================================*/
-#if HAL_USBH_USE_IAD
-static usbh_baseclassdriver_t *iad_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem);
-static void iad_unload(usbh_baseclassdriver_t *drv);
-static const usbh_classdriver_vmt_t usbhiadClassDriverVMT = {
- iad_load,
- iad_unload
-};
-static const usbh_classdriverinfo_t usbhiadClassDriverInfo = {
- 0xef, 0x02, 0x01, "IAD", &usbhiadClassDriverVMT
-};
-static usbh_baseclassdriver_t *iad_load(usbh_device_t *dev,
- const uint8_t *descriptor, uint16_t rem) {
- (void)rem;
+bool _usbh_match_vid_pid(usbh_device_t *dev, int32_t vid, int32_t pid) {
+ if (((vid < 0) || (dev->devDesc.idVendor == vid))
+ && ((pid < 0) || (dev->devDesc.idProduct == pid)))
+ return HAL_SUCCESS;
- if (descriptor[1] != USBH_DT_DEVICE)
- return 0;
+ return HAL_FAILED;
+}
- uinfo("Load a driver for each IF collection.");
+bool _usbh_match_descriptor(const uint8_t *descriptor, uint16_t rem,
+ int16_t type, int16_t _class, int16_t subclass, int16_t protocol) {
- generic_iterator_t icfg;
- if_iterator_t iif;
- const usbh_ia_descriptor_t *last_iad = 0;
+ int16_t dclass, dsubclass, dprotocol;
- cfg_iter_init(&icfg, dev->fullConfigurationDescriptor,
- dev->basicConfigDesc.wTotalLength);
- if (!icfg.valid) {
- uerr("Invalid configuration descriptor.");
- return 0;
- }
+ if ((rem < descriptor[0]) || (rem < 2))
+ return HAL_FAILED;
- for (if_iter_init(&iif, &icfg); iif.valid; if_iter_next(&iif)) {
- if (iif.iad && (iif.iad != last_iad)) {
- last_iad = iif.iad;
- if (_classdriver_load(dev, iif.iad->bFunctionClass,
- iif.iad->bFunctionSubClass,
- iif.iad->bFunctionProtocol,
- (uint8_t *)iif.iad,
- (uint8_t *)iif.curr - (uint8_t *)iif.iad + iif.rem) != HAL_SUCCESS) {
- uwarnf("No drivers found for IF collection #%d:%d",
- iif.iad->bFirstInterface,
- iif.iad->bFirstInterface + iif.iad->bInterfaceCount - 1);
- }
- }
- }
+ uint8_t dtype = descriptor[1];
- return 0;
-}
+ if ((type >= 0) && (type != dtype))
+ return HAL_FAILED;
-static void iad_unload(usbh_baseclassdriver_t *drv) {
- (void)drv;
-}
-#endif
+ switch (dtype) {
+ case USBH_DT_DEVICE: {
+ if (rem < USBH_DT_DEVICE_SIZE)
+ return HAL_FAILED;
+ const usbh_device_descriptor_t *const desc = (const usbh_device_descriptor_t *)descriptor;
+ dclass = desc->bDeviceClass;
+ dsubclass = desc->bDeviceSubClass;
+ dprotocol = desc->bDeviceProtocol;
+ } break;
+ case USBH_DT_INTERFACE: {
+ if (rem < USBH_DT_INTERFACE_SIZE)
+ return HAL_FAILED;
+ const usbh_interface_descriptor_t *const desc = (const usbh_interface_descriptor_t *)descriptor;
+ dclass = desc->bInterfaceClass;
+ dsubclass = desc->bInterfaceSubClass;
+ dprotocol = desc->bInterfaceProtocol;
+ } break;
+ case USBH_DT_INTERFACE_ASSOCIATION: {
+ if (rem < USBH_DT_INTERFACE_ASSOCIATION_SIZE)
+ return HAL_FAILED;
+ const usbh_ia_descriptor_t *const desc = (const usbh_ia_descriptor_t *)descriptor;
+ dclass = desc->bFunctionClass;
+ dsubclass = desc->bFunctionSubClass;
+ dprotocol = desc->bFunctionProtocol;
+ } break;
+ default:
+ return HAL_FAILED;
+ }
+ if (((_class < 0) || (_class == dclass))
+ && ((subclass < 0) || (subclass == dsubclass))
+ && ((protocol < 0) || (protocol == dprotocol)))
+ return HAL_SUCCESS;
-/*===========================================================================*/
-/* Class driver loader. */
-/*===========================================================================*/
+ return HAL_FAILED;
+}
static const usbh_classdriverinfo_t *usbh_classdrivers_lookup[] = {
+#if HAL_USBH_USE_ADDITIONAL_CLASS_DRIVERS
+ /* user-defined out of tree class drivers */
+ HAL_USBH_ADDITIONAL_CLASS_DRIVERS
+#endif
#if HAL_USBH_USE_FTDI
&usbhftdiClassDriverInfo,
#endif
-#if HAL_USBH_USE_IAD
- &usbhiadClassDriverInfo,
+#if HAL_USBH_USE_HUB
+ &usbhhubClassDriverInfo,
#endif
#if HAL_USBH_USE_UVC
&usbhuvcClassDriverInfo,
@@ -1274,48 +1262,39 @@ static const usbh_classdriverinfo_t *usbh_classdrivers_lookup[] = {
#if HAL_USBH_USE_MSD
&usbhmsdClassDriverInfo,
#endif
-#if HAL_USBH_USE_HUB
- &usbhhubClassDriverInfo
+#if HAL_USBH_USE_HID
+ &usbhhidClassDriverInfo,
+#endif
+#if HAL_USBH_USE_UVC
+ &usbhuvcClassDriverInfo,
+#endif
+#if HAL_USBH_USE_AOA
+ &usbhaoaClassDriverInfo, /* Leave always last */
#endif
};
-static bool _classdriver_load(usbh_device_t *dev, uint8_t class,
- uint8_t subclass, uint8_t protocol, uint8_t *descbuff, uint16_t rem) {
+static bool _classdriver_load(usbh_device_t *dev, uint8_t *descbuff, uint16_t rem) {
uint8_t i;
usbh_baseclassdriver_t *drv = NULL;
for (i = 0; i < sizeof_array(usbh_classdrivers_lookup); i++) {
const usbh_classdriverinfo_t *const info = usbh_classdrivers_lookup[i];
- if (class == 0xff) {
- /* vendor specific */
- if (info->class == 0xff) {
- uinfof("Try load vendor-specific driver %s", info->name);
- drv = info->vmt->load(dev, descbuff, rem);
- if (drv != NULL)
- goto success;
- }
- } else if ((info->class < 0) || ((info->class == class)
- && ((info->subclass < 0) || ((info->subclass == subclass)
- && ((info->protocol < 0) || (info->protocol == protocol)))))) {
- uinfof("Try load driver %s", info->name);
- drv = info->vmt->load(dev, descbuff, rem);
-#if HAL_USBH_USE_IAD
- /* special case: */
- if (info == &usbhiadClassDriverInfo)
- return HAL_SUCCESS;
-#endif
+ uinfof("Try load driver %s", info->name);
+ drv = info->vmt->load(dev, descbuff, rem);
- if (drv != NULL)
- goto success;
- }
+ if (drv != NULL)
+ goto success;
}
+
return HAL_FAILED;
success:
/* Link this driver to the device */
- drv->next = dev->drivers;
- dev->drivers = drv;
- drv->dev = dev;
+ if (!drv->dev) {
+ drv->next = dev->drivers;
+ dev->drivers = drv;
+ drv->dev = dev;
+ }
return HAL_SUCCESS;
}
@@ -1347,13 +1326,16 @@ static void _classdriver_process_device(usbh_device_t *dev) {
usbhDevicePrintConfiguration(dev->fullConfigurationDescriptor,
dev->basicConfigDesc.wTotalLength);
- if (devdesc->bDeviceClass == 0) {
- /* each interface defines its own device class/subclass/protocol */
- uinfo("Load a driver for each IF.");
+#if HAL_USBH_USE_IAD
+ if (dev->devDesc.bDeviceClass == 0xef
+ && dev->devDesc.bDeviceSubClass == 0x02
+ && dev->devDesc.bDeviceProtocol == 0x01) {
+
+ uinfo("Load a driver for each IF collection.");
generic_iterator_t icfg;
if_iterator_t iif;
- uint8_t last_if = 0xff;
+ const usbh_ia_descriptor_t *last_iad = 0;
cfg_iter_init(&icfg, dev->fullConfigurationDescriptor,
dev->basicConfigDesc.wTotalLength);
@@ -1363,24 +1345,49 @@ static void _classdriver_process_device(usbh_device_t *dev) {
}
for (if_iter_init(&iif, &icfg); iif.valid; if_iter_next(&iif)) {
- const usbh_interface_descriptor_t *const ifdesc = if_get(&iif);
- if (ifdesc->bInterfaceNumber != last_if) {
- last_if = ifdesc->bInterfaceNumber;
- if (_classdriver_load(dev, ifdesc->bInterfaceClass,
- ifdesc->bInterfaceSubClass,
- ifdesc->bInterfaceProtocol,
- (uint8_t *)ifdesc, iif.rem) != HAL_SUCCESS) {
- uwarnf("No drivers found for IF #%d", ifdesc->bInterfaceNumber);
+ if (iif.iad && (iif.iad != last_iad)) {
+ last_iad = iif.iad;
+ if (_classdriver_load(dev,
+ (uint8_t *)iif.iad,
+ (uint8_t *)iif.curr - (uint8_t *)iif.iad + iif.rem) != HAL_SUCCESS) {
+ uwarnf("No drivers found for IF collection #%d:%d",
+ iif.iad->bFirstInterface,
+ iif.iad->bFirstInterface + iif.iad->bInterfaceCount - 1);
}
}
}
- } else {
- if (_classdriver_load(dev, devdesc->bDeviceClass,
- devdesc->bDeviceSubClass,
- devdesc->bDeviceProtocol,
- (uint8_t *)devdesc, USBH_DT_DEVICE_SIZE) != HAL_SUCCESS) {
- uwarn("No drivers found.");
+ } else
+#endif
+ if (_classdriver_load(dev, (uint8_t *)devdesc, USBH_DT_DEVICE_SIZE) != HAL_SUCCESS) {
+ uinfo("No drivers found for device.");
+
+ if (devdesc->bDeviceClass == 0) {
+ /* each interface defines its own device class/subclass/protocol */
+ uinfo("Try load a driver for each IF.");
+
+ generic_iterator_t icfg;
+ if_iterator_t iif;
+ uint8_t last_if = 0xff;
+
+ cfg_iter_init(&icfg, dev->fullConfigurationDescriptor,
+ dev->basicConfigDesc.wTotalLength);
+ if (!icfg.valid) {
+ uerr("Invalid configuration descriptor.");
+ goto exit;
+ }
+
+ for (if_iter_init(&iif, &icfg); iif.valid; if_iter_next(&iif)) {
+ const usbh_interface_descriptor_t *const ifdesc = if_get(&iif);
+ if (ifdesc->bInterfaceNumber != last_if) {
+ last_if = ifdesc->bInterfaceNumber;
+ if (_classdriver_load(dev, (uint8_t *)ifdesc, iif.rem) != HAL_SUCCESS) {
+ uwarnf("No drivers found for IF #%d", ifdesc->bInterfaceNumber);
+ }
+ }
+ }
+ } else {
+ uwarn("Unable to load driver.");
}
}
@@ -1390,6 +1397,15 @@ exit:
}
}
+void usbhInit(void) {
+ uint8_t i;
+ for (i = 0; i < sizeof_array(usbh_classdrivers_lookup); i++) {
+ if (usbh_classdrivers_lookup[i]->vmt->init) {
+ usbh_classdrivers_lookup[i]->vmt->init();
+ }
+ }
+ usbh_lld_init();
+}
#endif
diff --git a/os/hal/src/usbh/TODO.txt b/os/hal/src/usbh/TODO.txt
new file mode 100644
index 0000000..87269be
--- /dev/null
+++ b/os/hal/src/usbh/TODO.txt
@@ -0,0 +1,21 @@
+In decreasing order of priority:
+
+Bugs:
+- Synchronization on driver unload between usbhMainLoop and driver APIs
+ - MSD: ok
+ - AOA: not done
+ - HUB: ok
+ - FTDI: not done
+ - HID: ok
+ - UVC: not done
+
+
+Enhancements:
+- Way to return error from the load() functions in order to stop the enumeration process
+- Event sources from the low-level driver, in order to know when to call usbhMainLoop (from the low-level driver and from the HUB driver status callback)
+- Possibility of internal main loop
+- Linked list for drivers for dynamic registration
+- A way to automate matching (similar to linux)
+- Hooks to override driver loading and to inform the user of problems
+- for STM32 LLD: think of a way to prevent Bulk IN NAK interrupt flood.
+- Integrate VBUS power switching functionality to the API.
diff --git a/os/hal/src/usbh/hal_usbh_aoa.c b/os/hal/src/usbh/hal_usbh_aoa.c
new file mode 100644
index 0000000..d565595
--- /dev/null
+++ b/os/hal/src/usbh/hal_usbh_aoa.c
@@ -0,0 +1,671 @@
+/*
+ ChibiOS - Copyright (C) 2006..2017 Giovanni Di Sirio
+ Copyright (C) 2015..2017 Diego Ismirlian, (dismirlian (at) google's mail)
+
+ Licensed 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.
+*/
+
+#include "hal.h"
+
+#if HAL_USBH_USE_AOA
+
+#if !HAL_USE_USBH
+#error "USBHAOA needs USBH"
+#endif
+
+#include <string.h>
+#include "usbh/dev/aoa.h"
+#include "usbh/internal.h"
+
+//#pragma GCC optimize("Og")
+
+
+#if USBHAOA_DEBUG_ENABLE_TRACE
+#define udbgf(f, ...) usbDbgPrintf(f, ##__VA_ARGS__)
+#define udbg(f, ...) usbDbgPuts(f, ##__VA_ARGS__)
+#else
+#define udbgf(f, ...) do {} while(0)
+#define udbg(f, ...) do {} while(0)
+#endif
+
+#if USBHAOA_DEBUG_ENABLE_INFO
+#define uinfof(f, ...) usbDbgPrintf(f, ##__VA_ARGS__)
+#define uinfo(f, ...) usbDbgPuts(f, ##__VA_ARGS__)
+#else
+#define uinfof(f, ...) do {} while(0)
+#define uinfo(f, ...) do {} while(0)
+#endif
+
+#if USBHAOA_DEBUG_ENABLE_WARNINGS
+#define uwarnf(f, ...) usbDbgPrintf(f, ##__VA_ARGS__)
+#define uwarn(f, ...) usbDbgPuts(f, ##__VA_ARGS__)
+#else
+#define uwarnf(f, ...) do {} while(0)
+#define uwarn(f, ...) do {} while(0)
+#endif
+
+#if USBHAOA_DEBUG_ENABLE_ERRORS
+#define uerrf(f, ...) usbDbgPrintf(f, ##__VA_ARGS__)
+#define uerr(f, ...) usbDbgPuts(f, ##__VA_ARGS__)
+#else
+#define uerrf(f, ...) do {} while(0)
+#define uerr(f, ...) do {} while(0)
+#endif
+
+
+/*===========================================================================*/
+/* Constants */
+/*===========================================================================*/
+
+#if !defined(HAL_USBHAOA_DEFAULT_MANUFACTURER)
+#define HAL_USBHAOA_DEFAULT_MANUFACTURER "ChibiOS"
+#endif
+
+#if !defined(HAL_USBHAOA_DEFAULT_MODEL)
+#define HAL_USBHAOA_DEFAULT_MODEL "USBH AOA Driver"
+#endif
+
+#if !defined(HAL_USBHAOA_DEFAULT_DESCRIPTION)
+#define HAL_USBHAOA_DEFAULT_DESCRIPTION "ChibiOS USBH AOA Driver"
+#endif
+
+#if !defined(HAL_USBHAOA_DEFAULT_VERSION)
+#define HAL_USBHAOA_DEFAULT_VERSION CH_KERNEL_VERSION
+#endif
+
+#if !defined(HAL_USBHAOA_DEFAULT_URI)
+#define HAL_USBHAOA_DEFAULT_URI NULL
+#endif
+
+#if !defined(HAL_USBHAOA_DEFAULT_SERIAL)
+#define HAL_USBHAOA_DEFAULT_SERIAL NULL
+#endif
+
+#if !defined(HAL_USBHAOA_DEFAULT_AUDIO_MODE)
+#define HAL_USBHAOA_DEFAULT_AUDIO_MODE USBHAOA_AUDIO_MODE_DISABLED
+#endif
+
+#define AOA_GOOGLE_VID 0x18D1
+#define AOA_GOOGLE_PID_ACCESSORY 0x2D00
+#define AOA_GOOGLE_PID_ACCESSORY_ABD 0x2D01
+#define AOA_GOOGLE_PID_AUDIO 0x2D02
+#define AOA_GOOGLE_PID_AUDIO_ABD 0x2D03
+#define AOA_GOOGLE_PID_ACCESSORY_AUDIO 0x2D04
+#define AOA_GOOGLE_PID_ACCESSORY_AUDIO_ABD 0x2D05
+
+#define AOA_ACCESSORY_GET_PROTOCOL 51
+#define AOA_ACCESSORY_SEND_STRING 52
+#define AOA_ACCESSORY_START 53
+
+#define AOA_SET_AUDIO_MODE 58
+
+static bool _get_protocol(usbh_device_t *dev, uint16_t *protocol);
+static bool _accessory_start(usbh_device_t *dev);
+static bool _set_audio_mode(usbh_device_t *dev, uint16_t mode);
+static bool _send_string(usbh_device_t *dev, uint8_t index, const char *string);
+
+
+static void _stop_channelS(USBHAOAChannel *aoacp);
+
+/*===========================================================================*/
+/* USB Class driver loader for AOA */
+/*===========================================================================*/
+USBHAOADriver USBHAOAD[HAL_USBHAOA_MAX_INSTANCES];
+
+static usbh_baseclassdriver_t *_aoa_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem);
+static void _aoa_unload(usbh_baseclassdriver_t *drv);
+static void _aoa_init(void);
+
+static const usbh_classdriver_vmt_t class_driver_vmt = {
+ _aoa_init,
+ _aoa_load,
+ _aoa_unload
+};
+
+const usbh_classdriverinfo_t usbhaoaClassDriverInfo = {
+ "AOA", &class_driver_vmt
+};
+
+#if defined(HAL_USBHAOA_FILTER_CALLBACK)
+extern usbhaoa_filter_callback_t HAL_USBHAOA_FILTER_CALLBACK;
+#endif
+
+static usbh_baseclassdriver_t *_aoa_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem) {
+ int i;
+ USBHAOADriver *aoap;
+
+ if (dev->devDesc.idVendor != AOA_GOOGLE_VID) {
+ uint16_t protocol;
+ static USBHAOAConfig config = {
+ {
+ HAL_USBHAOA_DEFAULT_MANUFACTURER,
+ HAL_USBHAOA_DEFAULT_MODEL,
+ HAL_USBHAOA_DEFAULT_DESCRIPTION,
+ HAL_USBHAOA_DEFAULT_VERSION,
+ HAL_USBHAOA_DEFAULT_URI,
+ HAL_USBHAOA_DEFAULT_SERIAL
+ },
+
+ {
+ HAL_USBHAOA_DEFAULT_AUDIO_MODE,
+ }
+ };
+
+ uinfo("AOA: Unrecognized VID");
+
+#if defined(HAL_USBHAOA_FILTER_CALLBACK)
+ if (!HAL_USBHAOA_FILTER_CALLBACK(dev, descriptor, rem, &config)) {
+ return NULL;
+ }
+#endif
+
+ uinfo("AOA: Try if it's an Android device");
+ if (_get_protocol(dev, &protocol) != HAL_SUCCESS) {
+ return NULL;
+ }
+ uinfof("AOA: Possible Android device found (protocol=%d)", protocol);
+
+ if (config.channel.manufacturer != NULL) {
+ if ((_send_string(dev, USBHAOA_ACCESSORY_STRING_MANUFACTURER, config.channel.manufacturer) != HAL_SUCCESS)
+ || (_send_string(dev, USBHAOA_ACCESSORY_STRING_MODEL, config.channel.model) != HAL_SUCCESS)
+ || (_send_string(dev, USBHAOA_ACCESSORY_STRING_DESCRIPTION, config.channel.description) != HAL_SUCCESS)
+ || (_send_string(dev, USBHAOA_ACCESSORY_STRING_VERSION, config.channel.version) != HAL_SUCCESS)
+ || (_send_string(dev, USBHAOA_ACCESSORY_STRING_URI, config.channel.uri) != HAL_SUCCESS)
+ || (_send_string(dev, USBHAOA_ACCESSORY_STRING_SERIAL, config.channel.serial) != HAL_SUCCESS)) {
+ uerr("AOA: Can't send string; abort start");
+ return NULL;
+ }
+ }
+
+ if (protocol > 1) {
+ if (_set_audio_mode(dev, (uint16_t)(config.audio.mode)) != HAL_SUCCESS) {
+ uerr("AOA: Can't set audio mode; abort channel start");
+ return NULL;
+ }
+ }
+
+ if (_accessory_start(dev) != HAL_SUCCESS) {
+ uerr("AOA: Can't start accessory; abort channel start");
+ }
+
+ return NULL;
+ }
+
+ /* AOAv2:
+ 0x2D00 accessory Provides two bulk endpoints for communicating with an Android application.
+ 0x2D01 accessory + adb For debugging purposes during accessory development. Available only if the user has enabled USB Debugging in the Android device settings.
+ 0x2D02 audio For streaming audio from an Android device to an accessory.
+ 0x2D03 audio + adb
+ 0x2D04 accessory + audio
+ 0x2D05 accessory + audio + adb
+ */
+
+ switch (dev->devDesc.idProduct) {
+ case AOA_GOOGLE_PID_ACCESSORY:
+ case AOA_GOOGLE_PID_ACCESSORY_ABD:
+// case AOA_GOOGLE_PID_AUDIO:
+// case AOA_GOOGLE_PID_AUDIO_ABD:
+ case AOA_GOOGLE_PID_ACCESSORY_AUDIO:
+ case AOA_GOOGLE_PID_ACCESSORY_AUDIO_ABD:
+ break;
+ default:
+ uerr("AOA: Unrecognized PID");
+ return NULL;
+ }
+
+ const usbh_interface_descriptor_t * const ifdesc = (const usbh_interface_descriptor_t *)descriptor;
+ if ((_usbh_match_descriptor(descriptor, rem, USBH_DT_INTERFACE, 0xFF, 0xFF, 0x00) != HAL_SUCCESS)
+ || (ifdesc->bNumEndpoints < 2)) {
+ uerr("AOA: This IF is not the Accessory IF");
+ return NULL;
+ }
+
+ uinfof("AOA: Found Accessory Interface #%d", ifdesc->bInterfaceNumber);
+
+ for (i = 0; i < HAL_USBHAOA_MAX_INSTANCES; i++) {
+ if (USBHAOAD[i].dev == NULL) {
+ aoap = &USBHAOAD[i];
+ goto alloc_ok;
+ }
+ }
+
+ uwarn("AOA: Can't alloc driver");
+
+ /* can't alloc */
+ return NULL;
+
+alloc_ok:
+ /* initialize the driver's variables */
+ usbhEPSetName(&dev->ctrl, "AOA[CTRL]");
+ aoap->state = USBHAOA_STATE_ACTIVE;
+
+ generic_iterator_t iep;
+ if_iterator_t iif;
+ iif.iad = 0;
+ iif.curr = descriptor;
+ iif.rem = rem;
+
+ aoap->channel.epin.status = USBH_EPSTATUS_UNINITIALIZED;
+ aoap->channel.epout.status = USBH_EPSTATUS_UNINITIALIZED;
+
+ for (ep_iter_init(&iep, &iif); iep.valid; ep_iter_next(&iep)) {
+ const usbh_endpoint_descriptor_t *const epdesc = ep_get(&iep);
+ if ((epdesc->bEndpointAddress & 0x80) && (epdesc->bmAttributes == USBH_EPTYPE_BULK)) {
+ uinfof("AOA: BULK IN endpoint found: bEndpointAddress=%02x", epdesc->bEndpointAddress);
+ usbhEPObjectInit(&aoap->channel.epin, dev, epdesc);
+ usbhEPSetName(&aoap->channel.epin, "AOA[BIN ]");
+ } else if (((epdesc->bEndpointAddress & 0x80) == 0)
+ && (epdesc->bmAttributes == USBH_EPTYPE_BULK)) {
+ uinfof("AOA: BULK OUT endpoint found: bEndpointAddress=%02x", epdesc->bEndpointAddress);
+ usbhEPObjectInit(&aoap->channel.epout, dev, epdesc);
+ usbhEPSetName(&aoap->channel.epout, "AOA[BOUT]");
+ } else {
+ uinfof("AOA: unsupported endpoint found: bEndpointAddress=%02x, bmAttributes=%02x",
+ epdesc->bEndpointAddress, epdesc->bmAttributes);
+ }
+ }
+
+ if ((aoap->channel.epin.status != USBH_EPSTATUS_CLOSED)
+ || (aoap->channel.epout.status != USBH_EPSTATUS_CLOSED)) {
+ uwarn("AOA: Couldn't find endpoints");
+ aoap->state = USBHAOA_STATE_STOP;
+ return NULL;
+ }
+
+ aoap->state = USBHAOA_STATE_READY;
+ aoap->channel.state = USBHAOA_CHANNEL_STATE_ACTIVE;
+ uwarn("AOA: Ready");
+ return (usbh_baseclassdriver_t *)aoap;
+}
+
+static void _aoa_unload(usbh_baseclassdriver_t *drv) {
+ osalDbgCheck(drv != NULL);
+ USBHAOADriver *const aoap = (USBHAOADriver *)drv;
+ osalSysLock();
+ _stop_channelS(&aoap->channel);
+ aoap->channel.state = USBHAOA_CHANNEL_STATE_STOP;
+ aoap->state = USBHAOA_STATE_STOP;
+ osalSysUnlock();
+}
+
+/* ------------------------------------ */
+/* Accessory data channel */
+/* ------------------------------------ */
+
+static void _submitOutI(USBHAOAChannel *aoacp, uint32_t len) {
+ udbgf("AOA: Submit OUT %d", len);
+ aoacp->oq_urb.requestedLength = len;
+ usbhURBObjectResetI(&aoacp->oq_urb);
+ usbhURBSubmitI(&aoacp->oq_urb);
+}
+
+static void _out_cb(usbh_urb_t *urb) {
+ USBHAOAChannel *const aoacp = (USBHAOAChannel *)urb->userData;
+ switch (urb->status) {
+ case USBH_URBSTATUS_OK:
+ aoacp->oq_ptr = aoacp->oq_buff;
+ aoacp->oq_counter = 64;
+ chThdDequeueNextI(&aoacp->oq_waiting, Q_OK);
+ chnAddFlagsI(aoacp, CHN_OUTPUT_EMPTY | CHN_TRANSMISSION_END);
+ return;
+ case USBH_URBSTATUS_DISCONNECTED:
+ uwarn("AOA: URB OUT disconnected");
+ chThdDequeueNextI(&aoacp->oq_waiting, Q_RESET);
+ chnAddFlagsI(aoacp, CHN_OUTPUT_EMPTY);
+ return;
+ default:
+ uerrf("AOA: URB OUT status unexpected = %d", urb->status);
+ break;
+ }
+ usbhURBObjectResetI(&aoacp->oq_urb);
+ usbhURBSubmitI(&aoacp->oq_urb);
+}
+
+static size_t _write_timeout(USBHAOAChannel *aoacp, const uint8_t *bp,
+ size_t n, systime_t timeout) {
+ chDbgCheck(n > 0U);
+
+ size_t w = 0;
+ osalSysLock();
+ while (true) {
+ if (aoacp->state != USBHAOA_CHANNEL_STATE_READY) {
+ osalSysUnlock();
+ return w;
+ }
+ while (usbhURBIsBusy(&aoacp->oq_urb)) {
+ if (chThdEnqueueTimeoutS(&aoacp->oq_waiting, timeout) != Q_OK) {
+ osalSysUnlock();
+ return w;
+ }
+ }
+
+ *aoacp->oq_ptr++ = *bp++;
+ if (--aoacp->oq_counter == 0) {
+ _submitOutI(aoacp, 64);
+ osalOsRescheduleS();
+ }
+ osalSysUnlock(); /* Gives a preemption chance in a controlled point.*/
+
+ w++;
+ if (--n == 0U)
+ return w;
+
+ osalSysLock();
+ }
+}
+
+static msg_t _put_timeout(USBHAOAChannel *aoacp, uint8_t b, systime_t timeout) {
+
+ osalSysLock();
+ if (aoacp->state != USBHAOA_CHANNEL_STATE_READY) {
+ osalSysUnlock();
+ return Q_RESET;
+ }
+
+ while (usbhURBIsBusy(&aoacp->oq_urb)) {
+ msg_t msg = chThdEnqueueTimeoutS(&aoacp->oq_waiting, timeout);
+ if (msg < Q_OK) {
+ osalSysUnlock();
+ return msg;
+ }
+ }
+
+ *aoacp->oq_ptr++ = b;
+ if (--aoacp->oq_counter == 0) {
+ _submitOutI(aoacp, 64);
+ osalOsRescheduleS();
+ }
+ osalSysUnlock();
+ return Q_OK;
+}
+
+static size_t _write(USBHAOAChannel *aoacp, const uint8_t *bp, size_t n) {
+ return _write_timeout(aoacp, bp, n, TIME_INFINITE);
+}
+
+static msg_t _put(USBHAOAChannel *aoacp, uint8_t b) {
+ return _put_timeout(aoacp, b, TIME_INFINITE);
+}
+
+static void _submitInI(USBHAOAChannel *aoacp) {
+ udbg("AOA: Submit IN");
+ usbhURBObjectResetI(&aoacp->iq_urb);
+ usbhURBSubmitI(&aoacp->iq_urb);
+}
+
+static void _in_cb(usbh_urb_t *urb) {
+ USBHAOAChannel *const aoacp = (USBHAOAChannel *)urb->userData;
+ switch (urb->status) {
+ case USBH_URBSTATUS_OK:
+ if (urb->actualLength == 0) {
+ udbgf("AOA: URB IN no data");
+ } else {
+ udbgf("AOA: URB IN data len=%d", urb->actualLength);
+ aoacp->iq_ptr = aoacp->iq_buff;
+ aoacp->iq_counter = urb->actualLength;
+ chThdDequeueNextI(&aoacp->iq_waiting, Q_OK);
+ chnAddFlagsI(aoacp, CHN_INPUT_AVAILABLE);
+ }
+ break;
+ case USBH_URBSTATUS_DISCONNECTED:
+ uwarn("AOA: URB IN disconnected");
+ chThdDequeueNextI(&aoacp->iq_waiting, Q_RESET);
+ break;
+ default:
+ uerrf("AOA: URB IN status unexpected = %d", urb->status);
+ _submitInI(aoacp);
+ break;
+ }
+}
+
+static size_t _read_timeout(USBHAOAChannel *aoacp, uint8_t *bp,
+ size_t n, systime_t timeout) {
+ size_t r = 0;
+
+ chDbgCheck(n > 0U);
+
+ osalSysLock();
+ while (true) {
+ if (aoacp->state != USBHAOA_CHANNEL_STATE_READY) {
+ osalSysUnlock();
+ return r;
+ }
+ while (aoacp->iq_counter == 0) {
+ if (!usbhURBIsBusy(&aoacp->iq_urb))
+ _submitInI(aoacp);
+ if (chThdEnqueueTimeoutS(&aoacp->iq_waiting, timeout) != Q_OK) {
+ osalSysUnlock();
+ return r;
+ }
+ }
+ *bp++ = *aoacp->iq_ptr++;
+ if (--aoacp->iq_counter == 0) {
+ _submitInI(aoacp);
+ osalOsRescheduleS();
+ }
+ osalSysUnlock();
+
+ r++;
+ if (--n == 0U)
+ return r;
+
+ osalSysLock();
+ }
+}
+
+static msg_t _get_timeout(USBHAOAChannel *aoacp, systime_t timeout) {
+ uint8_t b;
+
+ osalSysLock();
+ if (aoacp->state != USBHAOA_CHANNEL_STATE_READY) {
+ osalSysUnlock();
+ return Q_RESET;
+ }
+ while (aoacp->iq_counter == 0) {
+ if (!usbhURBIsBusy(&aoacp->iq_urb))
+ _submitInI(aoacp);
+ msg_t msg = chThdEnqueueTimeoutS(&aoacp->iq_waiting, timeout);
+ if (msg < Q_OK) {
+ osalSysUnlock();
+ return msg;
+ }
+ }
+ b = *aoacp->iq_ptr++;
+ if (--aoacp->iq_counter == 0) {
+ _submitInI(aoacp);
+ osalOsRescheduleS();
+ }
+ osalSysUnlock();
+
+ return (msg_t)b;
+}
+
+static msg_t _get(USBHAOAChannel *aoacp) {
+ return _get_timeout(aoacp, TIME_INFINITE);
+}
+
+static size_t _read(USBHAOAChannel *aoacp, uint8_t *bp, size_t n) {
+ return _read_timeout(aoacp, bp, n, TIME_INFINITE);
+}
+
+static const struct AOADriverVMT async_channel_vmt = {
+ (size_t (*)(void *, const uint8_t *, size_t))_write,
+ (size_t (*)(void *, uint8_t *, size_t))_read,
+ (msg_t (*)(void *, uint8_t))_put,
+ (msg_t (*)(void *))_get,
+ (msg_t (*)(void *, uint8_t, systime_t))_put_timeout,
+ (msg_t (*)(void *, systime_t))_get_timeout,
+ (size_t (*)(void *, const uint8_t *, size_t, systime_t))_write_timeout,
+ (size_t (*)(void *, uint8_t *, size_t, systime_t))_read_timeout
+};
+
+static void _stop_channelS(USBHAOAChannel *aoacp) {
+ if (aoacp->state != USBHAOA_CHANNEL_STATE_READY)
+ return;
+ uwarn("AOA: Stop channel");
+ chVTResetI(&aoacp->vt);
+ usbhEPCloseS(&aoacp->epin);
+ usbhEPCloseS(&aoacp->epout);
+ chThdDequeueAllI(&aoacp->iq_waiting, Q_RESET);
+ chThdDequeueAllI(&aoacp->oq_waiting, Q_RESET);
+ chnAddFlagsI(aoacp, CHN_DISCONNECTED);
+ aoacp->state = USBHAOA_CHANNEL_STATE_ACTIVE;
+ osalOsRescheduleS();
+}
+
+static void _vt(void *p) {
+ USBHAOAChannel *const aoacp = (USBHAOAChannel *)p;
+ osalSysLockFromISR();
+ uint32_t len = aoacp->oq_ptr - aoacp->oq_buff;
+ if (len && !usbhURBIsBusy(&aoacp->oq_urb)) {
+ _submitOutI(aoacp, len);
+ }
+ if ((aoacp->iq_counter == 0) && !usbhURBIsBusy(&aoacp->iq_urb)) {
+ _submitInI(aoacp);
+ }
+ chVTSetI(&aoacp->vt, OSAL_MS2I(16), _vt, aoacp);
+ osalSysUnlockFromISR();
+}
+
+void usbhaoaChannelStart(USBHAOADriver *aoap) {
+
+ osalDbgCheck(aoap);
+
+ USBHAOAChannel *const aoacp = (USBHAOAChannel *)&aoap->channel;
+
+ osalDbgCheck(aoap->state == USBHAOA_STATE_READY);
+
+ osalDbgCheck((aoacp->state == USBHAOA_CHANNEL_STATE_ACTIVE)
+ || (aoacp->state == USBHAOA_CHANNEL_STATE_READY));
+
+ if (aoacp->state == USBHAOA_CHANNEL_STATE_READY)
+ return;
+
+ usbhURBObjectInit(&aoacp->oq_urb, &aoacp->epout, _out_cb, aoacp, aoacp->oq_buff, 0);
+ chThdQueueObjectInit(&aoacp->oq_waiting);
+ aoacp->oq_counter = 64;
+ aoacp->oq_ptr = aoacp->oq_buff;
+ usbhEPOpen(&aoacp->epout);
+
+ usbhURBObjectInit(&aoacp->iq_urb, &aoacp->epin, _in_cb, aoacp, aoacp->iq_buff, 64);
+ chThdQueueObjectInit(&aoacp->iq_waiting);
+ aoacp->iq_counter = 0;
+ aoacp->iq_ptr = aoacp->iq_buff;
+ usbhEPOpen(&aoacp->epin);
+ usbhURBSubmit(&aoacp->iq_urb);
+
+ chVTObjectInit(&aoacp->vt);
+ chVTSet(&aoacp->vt, OSAL_MS2I(16), _vt, aoacp);
+
+ aoacp->state = USBHAOA_CHANNEL_STATE_READY;
+
+ osalEventBroadcastFlags(&aoacp->event, CHN_CONNECTED | CHN_OUTPUT_EMPTY);
+}
+
+void usbhaoaChannelStop(USBHAOADriver *aoap) {
+ osalDbgCheck((aoap->channel.state == USBHAOA_CHANNEL_STATE_ACTIVE)
+ || (aoap->channel.state == USBHAOA_CHANNEL_STATE_READY));
+ osalSysLock();
+ _stop_channelS(&aoap->channel);
+ osalSysUnlock();
+}
+
+/* ------------------------------------ */
+/* General AOA functions */
+/* ------------------------------------ */
+static bool _get_protocol(usbh_device_t *dev, uint16_t *protocol) {
+ USBH_DEFINE_BUFFER(uint16_t proto);
+
+ usbh_urbstatus_t ret = usbhControlRequest(dev,
+ USBH_REQTYPE_DIR_IN | USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_RECIP_DEVICE,
+ AOA_ACCESSORY_GET_PROTOCOL,
+ 0,
+ 0,
+ 2,
+ (uint8_t *)&proto);
+
+ if ((ret != USBH_URBSTATUS_OK) || (proto > 2))
+ return HAL_FAILED;
+
+ *protocol = proto;
+ return HAL_SUCCESS;
+}
+
+static bool _accessory_start(usbh_device_t *dev) {
+ usbh_urbstatus_t ret = usbhControlRequest(dev,
+ USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_RECIP_DEVICE,
+ AOA_ACCESSORY_START,
+ 0,
+ 0,
+ 0,
+ NULL);
+
+ if (ret != USBH_URBSTATUS_OK)
+ return HAL_FAILED;
+
+ return HAL_SUCCESS;
+}
+
+static bool _set_audio_mode(usbh_device_t *dev, uint16_t mode) {
+ usbh_urbstatus_t ret = usbhControlRequest(dev,
+ USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_RECIP_DEVICE,
+ AOA_SET_AUDIO_MODE,
+ mode,
+ 0,
+ 0,
+ NULL);
+
+ if (ret != USBH_URBSTATUS_OK)
+ return HAL_FAILED;
+
+ return HAL_SUCCESS;
+}
+
+static bool _send_string(usbh_device_t *dev, uint8_t index, const char *string)
+{
+ USBH_DEFINE_BUFFER(const char nullstr[1]) = {0};
+ if (string == NULL)
+ string = nullstr;
+
+ usbh_urbstatus_t ret = usbhControlRequest(dev,
+ USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_RECIP_DEVICE,
+ AOA_ACCESSORY_SEND_STRING,
+ 0,
+ index,
+ strlen(string) + 1,
+ (uint8_t *)string);
+
+ if (ret != USBH_URBSTATUS_OK)
+ return HAL_FAILED;
+
+ return HAL_SUCCESS;
+}
+
+static void _object_init(USBHAOADriver *aoap) {
+ osalDbgCheck(aoap != NULL);
+ memset(aoap, 0, sizeof(*aoap));
+ aoap->info = &usbhaoaClassDriverInfo;
+ aoap->state = USBHAOA_STATE_STOP;
+ aoap->channel.vmt = &async_channel_vmt;
+ osalEventObjectInit(&aoap->channel.event);
+ aoap->channel.state = USBHAOA_CHANNEL_STATE_STOP;
+}
+
+static void _aoa_init(void) {
+ uint8_t i;
+ for (i = 0; i < HAL_USBHAOA_MAX_INSTANCES; i++) {
+ _object_init(&USBHAOAD[i]);
+ }
+}
+
+#endif
diff --git a/os/hal/src/usbh/hal_usbh_debug.c b/os/hal/src/usbh/hal_usbh_debug.c
index 9f17189..d32f1c6 100644
--- a/os/hal/src/usbh/hal_usbh_debug.c
+++ b/os/hal/src/usbh/hal_usbh_debug.c
@@ -1,6 +1,6 @@
/*
- ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio
- Copyright (C) 2015 Diego Ismirlian, TISA, (dismirlian (at) google's mail)
+ ChibiOS - Copyright (C) 2006..2017 Giovanni Di Sirio
+ Copyright (C) 2015..2017 Diego Ismirlian, (dismirlian (at) google's mail)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,15 +17,13 @@
#include "hal.h"
-#if HAL_USE_USBH
+#if HAL_USE_USBH && USBH_DEBUG_ENABLE
#include "ch.h"
#include "usbh/debug.h"
#include <stdarg.h>
#include "chprintf.h"
-#if USBH_DEBUG_ENABLE
-
#define MAX_FILLER 11
#define FLOAT_PRECISION 9
#define MPRINTF_USE_FLOAT 0
@@ -108,17 +106,18 @@ static char *ftoa(char *p, double num, unsigned long precision, bool dot) {
}
#endif
-static inline void _put(char c) {
- input_queue_t *iqp = &USBH_DEBUG_USBHD.iq;
-
- if (chIQIsFullI(iqp))
- return;
-
- iqp->q_counter++;
+static inline void _wr(input_queue_t *iqp, char c) {
*iqp->q_wrptr++ = c;
if (iqp->q_wrptr >= iqp->q_top)
iqp->q_wrptr = iqp->q_buffer;
+}
+static inline void _put(char c) {
+ input_queue_t *iqp = &USBH_DEBUG_USBHD.iq;
+ if (sizeof(USBH_DEBUG_USBHD.dbg_buff) - iqp->q_counter <= 1)
+ return;
+ iqp->q_counter++;
+ _wr(iqp, c);
}
int _dbg_printf(const char *fmt, va_list ap) {
@@ -343,19 +342,39 @@ unsigned_common:
}
-static void _print_hdr(void)
-{
+static systime_t first, last;
+static bool ena;
+static uint32_t hdr[2];
+
+static void _build_hdr(void) {
uint32_t hfnum = USBH_DEBUG_USBHD.otg->HFNUM;
uint16_t hfir = USBH_DEBUG_USBHD.otg->HFIR;
+ last = osalOsGetSystemTimeX();
+ if (ena) {
+ first = last;
+ }
- _put(0xff);
- _put(0xff);
- _put(hfir & 0xff);
- _put(hfir >> 8);
- _put(hfnum & 0xff);
- _put((hfnum >> 8) & 0xff);
- _put((hfnum >> 16) & 0xff);
- _put((hfnum >> 24) & 0xff);
+ if (((hfnum & 0x3fff) == 0x3fff) && (hfir == (hfnum >> 16))) {
+ hdr[0] = 0xfeff;
+ hdr[1] = last - first;
+ ena = FALSE;
+ } else {
+ hdr[0] = 0xffff | (hfir << 16);
+ hdr[1] = hfnum;
+ ena = TRUE;
+ }
+}
+
+static void _print_hdr(void)
+{
+ _put(hdr[0] & 0xff);
+ _put((hdr[0] >> 8) & 0xff);
+ _put((hdr[0] >> 16) & 0xff);
+ _put((hdr[0] >> 24) & 0xff);
+ _put(hdr[1] & 0xff);
+ _put((hdr[1] >> 8) & 0xff);
+ _put((hdr[1] >> 16) & 0xff);
+ _put((hdr[1] >> 24) & 0xff);
}
void usbDbgPrintf(const char *fmt, ...)
@@ -363,10 +382,16 @@ void usbDbgPrintf(const char *fmt, ...)
va_list ap;
va_start(ap, fmt);
syssts_t sts = chSysGetStatusAndLockX();
- _print_hdr();
- _dbg_printf(fmt, ap);
- _put(0);
- chThdDequeueNextI(&USBH_DEBUG_USBHD.iq.q_waiting, Q_OK);
+ input_queue_t *iqp = &USBH_DEBUG_USBHD.iq;
+ int rem = sizeof(USBH_DEBUG_USBHD.dbg_buff) - iqp->q_counter;
+ if (rem >= 9) {
+ _build_hdr();
+ _print_hdr();
+ _dbg_printf(fmt, ap);
+ iqp->q_counter++;
+ _wr(iqp, 0);
+ chThdDequeueNextI(&USBH_DEBUG_USBHD.iq.q_waiting, Q_OK);
+ }
chSysRestoreStatusX(sts);
va_end(ap);
}
@@ -374,32 +399,28 @@ void usbDbgPrintf(const char *fmt, ...)
void usbDbgPuts(const char *s)
{
- uint32_t buff[2] = {
- 0xffff | (USBH_DEBUG_USBHD.otg->HFIR << 16),
- USBH_DEBUG_USBHD.otg->HFNUM
- };
- uint8_t *p = (uint8_t *)buff;
+ _build_hdr();
+ uint8_t *p = (uint8_t *)hdr;
uint8_t *top = p + 8;
syssts_t sts = chSysGetStatusAndLockX();
input_queue_t *iqp = &USBH_DEBUG_USBHD.iq;
int rem = sizeof(USBH_DEBUG_USBHD.dbg_buff) - iqp->q_counter;
- while (rem) {
- *iqp->q_wrptr++ = *p;
- if (iqp->q_wrptr >= iqp->q_top)
- iqp->q_wrptr = iqp->q_buffer;
- rem--;
- if (++p == top) break;
- }
- while (rem) {
- *iqp->q_wrptr++ = *s;
- if (iqp->q_wrptr >= iqp->q_top)
- iqp->q_wrptr = iqp->q_buffer;
- rem--;
- if (!*s++) break;
+ if (rem >= 9) {
+ while (rem) {
+ _wr(iqp, *p);
+ if (++p == top) break;
+ }
+ rem -= 9;
+ while (rem && *s) {
+ _wr(iqp, *s);
+ rem--;
+ s++;
+ }
+ _wr(iqp, 0);
+ iqp->q_counter = sizeof(USBH_DEBUG_USBHD.dbg_buff) - rem;
+ chThdDequeueNextI(&USBH_DEBUG_USBHD.iq.q_waiting, Q_OK);
}
- iqp->q_counter = sizeof(USBH_DEBUG_USBHD.dbg_buff) - rem;
- chThdDequeueNextI(&USBH_DEBUG_USBHD.iq.q_waiting, Q_OK);
chSysRestoreStatusX(sts);
}
@@ -407,8 +428,8 @@ void usbDbgReset(void) {
const char *msg = "\r\n\r\n==== DEBUG OUTPUT RESET ====\r\n";
syssts_t sts = chSysGetStatusAndLockX();
- chIQResetI(&USBH_DEBUG_USBHD.iq);
- chOQResetI(&USBH_DEBUG_SD.oqueue);
+ iqResetI(&USBH_DEBUG_USBHD.iq);
+ oqResetI(&USBH_DEBUG_SD.oqueue);
while (*msg) {
*USBH_DEBUG_SD.oqueue.q_wrptr++ = *msg++;
USBH_DEBUG_SD.oqueue.q_counter--;
@@ -431,8 +452,8 @@ void usbDbgSystemHalted(void) {
if (!((bool)((USBH_DEBUG_SD.oqueue.q_wrptr == USBH_DEBUG_SD.oqueue.q_rdptr) && (USBH_DEBUG_SD.oqueue.q_counter != 0U))))
break;
USBH_DEBUG_SD.oqueue.q_counter++;
- while (!(USART1->SR & USART_SR_TXE));
- USART1->DR = *USBH_DEBUG_SD.oqueue.q_rdptr++;
+ while (!(USBH_DEBUG_SD.usart->SR & USART_SR_TXE));
+ USBH_DEBUG_SD.usart->DR = *USBH_DEBUG_SD.oqueue.q_rdptr++;
if (USBH_DEBUG_SD.oqueue.q_rdptr >= USBH_DEBUG_SD.oqueue.q_top) {
USBH_DEBUG_SD.oqueue.q_rdptr = USBH_DEBUG_SD.oqueue.q_buffer;
}
@@ -458,57 +479,74 @@ void usbDbgSystemHalted(void) {
while (true) {
c = _get(); if (c < 0) return;
if (!c) {
- while (!(USART1->SR & USART_SR_TXE));
- USART1->DR = '\r';
- while (!(USART1->SR & USART_SR_TXE));
- USART1->DR = '\n';
+ while (!(USBH_DEBUG_SD.usart->SR & USART_SR_TXE));
+ USBH_DEBUG_SD.usart->DR = '\r';
+ while (!(USBH_DEBUG_SD.usart->SR & USART_SR_TXE));
+ USBH_DEBUG_SD.usart->DR = '\n';
state = 0;
break;
}
- while (!(USART1->SR & USART_SR_TXE));
- USART1->DR = c;
+ while (!(USBH_DEBUG_SD.usart->SR & USART_SR_TXE));
+ USBH_DEBUG_SD.usart->DR = c;
}
}
}
}
-static void usb_debug_thread(void *p) {
- USBHDriver *host = (USBHDriver *)p;
+static void usb_debug_thread(void *arg) {
+ USBHDriver *host = (USBHDriver *)arg;
uint8_t state = 0;
chRegSetThreadName("USBH_DBG");
while (true) {
- msg_t c = chIQGet(&host->iq);
+ msg_t c = iqGet(&host->iq);
if (c < 0) goto reset;
if (state == 0) {
if (c == 0xff) state = 1;
} else if (state == 1) {
if (c == 0xff) state = 2;
+ else if (c == 0xfe) state = 3;
else (state = 0);
- } else {
+ } else if (state == 2) {
uint16_t hfir;
uint32_t hfnum;
hfir = c;
- c = chIQGet(&host->iq); if (c < 0) goto reset;
+ c = iqGet(&host->iq); if (c < 0) goto reset;
hfir |= c << 8;
- c = chIQGet(&host->iq); if (c < 0) goto reset;
+ c = iqGet(&host->iq); if (c < 0) goto reset;
hfnum = c;
- c = chIQGet(&host->iq); if (c < 0) goto reset;
+ c = iqGet(&host->iq); if (c < 0) goto reset;
hfnum |= c << 8;
- c = chIQGet(&host->iq); if (c < 0) goto reset;
+ c = iqGet(&host->iq); if (c < 0) goto reset;
hfnum |= c << 16;
- c = chIQGet(&host->iq); if (c < 0) goto reset;
+ c = iqGet(&host->iq); if (c < 0) goto reset;
hfnum |= c << 24;
uint32_t f = hfnum & 0xffff;
uint32_t p = 1000 - ((hfnum >> 16) / (hfir / 1000));
- chprintf((BaseSequentialStream *)&USBH_DEBUG_SD, "%05d.%03d ", f, p);
-
+ chprintf((BaseSequentialStream *)&USBH_DEBUG_SD, "%05d.%03d ", f, p);
+ state = 4;
+ } else if (state == 3) {
+ uint32_t t;
+
+ c = iqGet(&host->iq); if (c < 0) goto reset;
+ c = iqGet(&host->iq); if (c < 0) goto reset;
+
+ t = c;
+ c = iqGet(&host->iq); if (c < 0) goto reset;
+ t |= c << 8;
+ c = iqGet(&host->iq); if (c < 0) goto reset;
+ t |= c << 16;
+ c = iqGet(&host->iq); if (c < 0) goto reset;
+ t |= c << 24;
+
+ chprintf((BaseSequentialStream *)&USBH_DEBUG_SD, "+%08d ", t);
+ state = 4;
+ } else {
while (true) {
- c = chIQGet(&host->iq); if (c < 0) goto reset;
if (!c) {
sdPut(&USBH_DEBUG_SD, '\r');
sdPut(&USBH_DEBUG_SD, '\n');
@@ -516,6 +554,7 @@ static void usb_debug_thread(void *p) {
break;
}
sdPut(&USBH_DEBUG_SD, (uint8_t)c);
+ c = iqGet(&host->iq); if (c < 0) goto reset;
}
}
@@ -528,9 +567,8 @@ reset:
void usbDbgInit(USBHDriver *host) {
if (host != &USBH_DEBUG_USBHD)
return;
- chIQObjectInit(&USBH_DEBUG_USBHD.iq, USBH_DEBUG_USBHD.dbg_buff, sizeof(USBH_DEBUG_USBHD.dbg_buff), 0, 0);
+ iqObjectInit(&USBH_DEBUG_USBHD.iq, USBH_DEBUG_USBHD.dbg_buff, sizeof(USBH_DEBUG_USBHD.dbg_buff), 0, 0);
chThdCreateStatic(USBH_DEBUG_USBHD.waDebug, sizeof(USBH_DEBUG_USBHD.waDebug), NORMALPRIO, usb_debug_thread, &USBH_DEBUG_USBHD);
}
-#endif
#endif
diff --git a/os/hal/src/usbh/hal_usbh_desciter.c b/os/hal/src/usbh/hal_usbh_desciter.c
index 63137d4..3695881 100644
--- a/os/hal/src/usbh/hal_usbh_desciter.c
+++ b/os/hal/src/usbh/hal_usbh_desciter.c
@@ -1,6 +1,6 @@
/*
- ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio
- Copyright (C) 2015 Diego Ismirlian, TISA, (dismirlian (at) google's mail)
+ ChibiOS - Copyright (C) 2006..2017 Giovanni Di Sirio
+ Copyright (C) 2015..2017 Diego Ismirlian, (dismirlian (at) google's mail)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -134,22 +134,18 @@ void cs_iter_next(generic_iterator_t *ics) {
if ((curr[0] < 2) || (rem < 2) || (rem < curr[0]))
return;
- //for (;;) {
- rem -= curr[0];
- curr += curr[0];
+ rem -= curr[0];
+ curr += curr[0];
- if ((curr[0] < 2) || (rem < 2) || (rem < curr[0]))
- return;
-
- if ((curr[1] == USBH_DT_INTERFACE_ASSOCIATION)
- || (curr[1] == USBH_DT_INTERFACE)
- || (curr[1] == USBH_DT_CONFIG)
- || (curr[1] == USBH_DT_ENDPOINT)) {
- return;
- }
+ if ((curr[0] < 2) || (rem < 2) || (rem < curr[0]))
+ return;
- // break;
- //}
+ if ((curr[1] == USBH_DT_INTERFACE_ASSOCIATION)
+ || (curr[1] == USBH_DT_INTERFACE)
+ || (curr[1] == USBH_DT_CONFIG)
+ || (curr[1] == USBH_DT_ENDPOINT)) {
+ return;
+ }
ics->valid = 1;
ics->rem = rem;
diff --git a/os/hal/src/usbh/hal_usbh_ftdi.c b/os/hal/src/usbh/hal_usbh_ftdi.c
index 4bd7296..6966028 100644
--- a/os/hal/src/usbh/hal_usbh_ftdi.c
+++ b/os/hal/src/usbh/hal_usbh_ftdi.c
@@ -1,6 +1,6 @@
/*
- ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio
- Copyright (C) 2015 Diego Ismirlian, TISA, (dismirlian (at) google's mail)
+ ChibiOS - Copyright (C) 2006..2017 Giovanni Di Sirio
+ Copyright (C) 2015..2017 Diego Ismirlian, (dismirlian (at) google's mail)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
*/
#include "hal.h"
-#include "hal_usbh.h"
#if HAL_USBH_USE_FTDI
@@ -28,9 +27,6 @@
#include "usbh/dev/ftdi.h"
#include "usbh/internal.h"
-//#pragma GCC optimize("Og")
-
-
#if USBHFTDI_DEBUG_ENABLE_TRACE
#define udbgf(f, ...) usbDbgPrintf(f, ##__VA_ARGS__)
#define udbg(f, ...) usbDbgPuts(f, ##__VA_ARGS__)
@@ -63,22 +59,25 @@
#define uerr(f, ...) do {} while(0)
#endif
+static void _ftdip_object_init(USBHFTDIPortDriver *ftdipp);
/*===========================================================================*/
/* USB Class driver loader for FTDI */
/*===========================================================================*/
USBHFTDIDriver USBHFTDID[HAL_USBHFTDI_MAX_INSTANCES];
+static void _ftdi_init(void);
static usbh_baseclassdriver_t *_ftdi_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem);
static void _ftdi_unload(usbh_baseclassdriver_t *drv);
static const usbh_classdriver_vmt_t class_driver_vmt = {
+ _ftdi_init,
_ftdi_load,
_ftdi_unload
};
const usbh_classdriverinfo_t usbhftdiClassDriverInfo = {
- 0xff, 0xff, 0xff, "FTDI", &class_driver_vmt
+ "FTDI", &class_driver_vmt
};
static USBHFTDIPortDriver *_find_port(void) {
@@ -94,10 +93,8 @@ static usbh_baseclassdriver_t *_ftdi_load(usbh_device_t *dev, const uint8_t *des
int i;
USBHFTDIDriver *ftdip;
- if (dev->devDesc.idVendor != 0x0403) {
- uerr("FTDI: Unrecognized VID");
+ if (_usbh_match_vid_pid(dev, 0x0403, -1) != HAL_SUCCESS)
return NULL;
- }
switch (dev->devDesc.idProduct) {
case 0x6001:
@@ -105,17 +102,18 @@ static usbh_baseclassdriver_t *_ftdi_load(usbh_device_t *dev, const uint8_t *des
case 0x6011:
case 0x6014:
case 0x6015:
+ case 0xE2E6:
break;
default:
uerr("FTDI: Unrecognized PID");
return NULL;
}
- if ((rem < descriptor[0]) || (descriptor[1] != USBH_DT_INTERFACE))
+ if (_usbh_match_descriptor(descriptor, rem, USBH_DT_INTERFACE,
+ 0xff, 0xff, 0xff) != HAL_SUCCESS)
return NULL;
- const usbh_interface_descriptor_t * const ifdesc = (const usbh_interface_descriptor_t * const)descriptor;
- if (ifdesc->bInterfaceNumber != 0) {
+ if (((const usbh_interface_descriptor_t *)descriptor)->bInterfaceNumber != 0) {
uwarn("FTDI: Will allocate driver along with IF #0");
}
@@ -211,7 +209,7 @@ alloc_ok:
}
-static void _stop(USBHFTDIPortDriver *ftdipp);
+static void _stopS(USBHFTDIPortDriver *ftdipp);
static void _ftdi_unload(usbh_baseclassdriver_t *drv) {
osalDbgCheck(drv != NULL);
USBHFTDIDriver *const ftdip = (USBHFTDIDriver *)drv;
@@ -219,7 +217,9 @@ static void _ftdi_unload(usbh_baseclassdriver_t *drv) {
osalMutexLock(&ftdip->mtx);
while (ftdipp) {
- _stop(ftdipp);
+ osalSysLock();
+ _stopS(ftdipp);
+ osalSysUnlock();
ftdipp = ftdipp->next;
}
@@ -227,7 +227,7 @@ static void _ftdi_unload(usbh_baseclassdriver_t *drv) {
osalSysLock();
while (ftdipp) {
USBHFTDIPortDriver *next = ftdipp->next;
- usbhftdipObjectInit(ftdipp);
+ _ftdip_object_init(ftdipp);
ftdipp = next;
}
osalSysUnlock();
@@ -314,17 +314,17 @@ static usbh_urbstatus_t _ftdi_port_control(USBHFTDIPortDriver *ftdipp,
uint8_t *buff) {
static const uint8_t bmRequestType[] = {
- USBH_REQTYPE_VENDOR | USBH_REQTYPE_OUT | USBH_REQTYPE_DEVICE, //0 FTDI_COMMAND_RESET
- USBH_REQTYPE_VENDOR | USBH_REQTYPE_OUT | USBH_REQTYPE_DEVICE, //1 FTDI_COMMAND_MODEMCTRL
- USBH_REQTYPE_VENDOR | USBH_REQTYPE_OUT | USBH_REQTYPE_DEVICE, //2 FTDI_COMMAND_SETFLOW
- USBH_REQTYPE_VENDOR | USBH_REQTYPE_OUT | USBH_REQTYPE_DEVICE, //3 FTDI_COMMAND_SETBAUD
- USBH_REQTYPE_VENDOR | USBH_REQTYPE_OUT | USBH_REQTYPE_DEVICE, //4 FTDI_COMMAND_SETDATA
+ USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_RECIP_DEVICE, //0 FTDI_COMMAND_RESET
+ USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_RECIP_DEVICE, //1 FTDI_COMMAND_MODEMCTRL
+ USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_RECIP_DEVICE, //2 FTDI_COMMAND_SETFLOW
+ USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_RECIP_DEVICE, //3 FTDI_COMMAND_SETBAUD
+ USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_RECIP_DEVICE, //4 FTDI_COMMAND_SETDATA
};
osalDbgCheck(bRequest < sizeof_array(bmRequestType));
osalDbgCheck(bRequest != 1);
- const USBH_DEFINE_BUFFER(usbh_control_request_t, req) = {
+ USBH_DEFINE_BUFFER(const usbh_control_request_t req) = {
bmRequestType[bRequest],
bRequest,
wValue,
@@ -332,7 +332,7 @@ static usbh_urbstatus_t _ftdi_port_control(USBHFTDIPortDriver *ftdipp,
wLength
};
- return usbhControlRequestExtended(ftdipp->ftdip->dev, &req, buff, NULL, MS2ST(1000));
+ return usbhControlRequestExtended(ftdipp->ftdip->dev, &req, buff, NULL, OSAL_MS2I(1000));
}
static uint32_t _get_divisor(uint32_t baud, usbhftdi_type_t type) {
@@ -387,14 +387,14 @@ static usbh_urbstatus_t _set_baudrate(USBHFTDIPortDriver *ftdipp, uint32_t baudr
if (ftdipp->ftdip->dev->basicConfigDesc.bNumInterfaces > 1)
wIndex = (wIndex << 8) | (ftdipp->ifnum + 1);
- const USBH_DEFINE_BUFFER(usbh_control_request_t, req) = {
- USBH_REQTYPE_VENDOR | USBH_REQTYPE_OUT | USBH_REQTYPE_DEVICE,
+ USBH_DEFINE_BUFFER(const usbh_control_request_t req) = {
+ USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_RECIP_DEVICE,
FTDI_COMMAND_SETBAUD,
wValue,
wIndex,
0
};
- return usbhControlRequestExtended(ftdipp->ftdip->dev, &req, NULL, NULL, MS2ST(1000));
+ return usbhControlRequestExtended(ftdipp->ftdip->dev, &req, NULL, NULL, OSAL_MS2I(1000));
}
@@ -415,7 +415,7 @@ static void _out_cb(usbh_urb_t *urb) {
return;
case USBH_URBSTATUS_DISCONNECTED:
uwarn("FTDI: URB OUT disconnected");
- chThdDequeueNextI(&ftdipp->oq_waiting, Q_RESET);
+ chThdDequeueAllI(&ftdipp->oq_waiting, Q_RESET);
return;
default:
uerrf("FTDI: URB OUT status unexpected = %d", urb->status);
@@ -430,15 +430,15 @@ static size_t _write_timeout(USBHFTDIPortDriver *ftdipp, const uint8_t *bp,
chDbgCheck(n > 0U);
size_t w = 0;
- chSysLock();
+ osalSysLock();
while (true) {
if (ftdipp->state != USBHFTDIP_STATE_READY) {
- chSysUnlock();
+ osalSysUnlock();
return w;
}
while (usbhURBIsBusy(&ftdipp->oq_urb)) {
if (chThdEnqueueTimeoutS(&ftdipp->oq_waiting, timeout) != Q_OK) {
- chSysUnlock();
+ osalSysUnlock();
return w;
}
}
@@ -446,30 +446,30 @@ static size_t _write_timeout(USBHFTDIPortDriver *ftdipp, const uint8_t *bp,
*ftdipp->oq_ptr++ = *bp++;
if (--ftdipp->oq_counter == 0) {
_submitOutI(ftdipp, 64);
- chSchRescheduleS();
+ osalOsRescheduleS();
}
- chSysUnlock(); /* Gives a preemption chance in a controlled point.*/
+ osalSysUnlock(); /* Gives a preemption chance in a controlled point.*/
w++;
if (--n == 0U)
return w;
- chSysLock();
+ osalSysLock();
}
}
static msg_t _put_timeout(USBHFTDIPortDriver *ftdipp, uint8_t b, systime_t timeout) {
- chSysLock();
+ osalSysLock();
if (ftdipp->state != USBHFTDIP_STATE_READY) {
- chSysUnlock();
+ osalSysUnlock();
return Q_RESET;
}
while (usbhURBIsBusy(&ftdipp->oq_urb)) {
msg_t msg = chThdEnqueueTimeoutS(&ftdipp->oq_waiting, timeout);
if (msg < Q_OK) {
- chSysUnlock();
+ osalSysUnlock();
return msg;
}
}
@@ -477,9 +477,9 @@ static msg_t _put_timeout(USBHFTDIPortDriver *ftdipp, uint8_t b, systime_t timeo
*ftdipp->oq_ptr++ = b;
if (--ftdipp->oq_counter == 0) {
_submitOutI(ftdipp, 64);
- chSchRescheduleS();
+ osalOsRescheduleS();
}
- chSysUnlock();
+ osalSysUnlock();
return Q_OK;
}
@@ -516,12 +516,12 @@ static void _in_cb(usbh_urb_t *urb) {
udbgf("FTDI: URB IN no data, status=%02x %02x",
((uint8_t *)urb->buff)[0],
((uint8_t *)urb->buff)[1]);
- return;
+ // return;
}
break;
case USBH_URBSTATUS_DISCONNECTED:
uwarn("FTDI: URB IN disconnected");
- chThdDequeueNextI(&ftdipp->iq_waiting, Q_RESET);
+ chThdDequeueAllI(&ftdipp->iq_waiting, Q_RESET);
return;
default:
uerrf("FTDI: URB IN status unexpected = %d", urb->status);
@@ -536,41 +536,41 @@ static size_t _read_timeout(USBHFTDIPortDriver *ftdipp, uint8_t *bp,
chDbgCheck(n > 0U);
- chSysLock();
+ osalSysLock();
while (true) {
if (ftdipp->state != USBHFTDIP_STATE_READY) {
- chSysUnlock();
+ osalSysUnlock();
return r;
}
while (ftdipp->iq_counter == 0) {
if (!usbhURBIsBusy(&ftdipp->iq_urb))
_submitInI(ftdipp);
if (chThdEnqueueTimeoutS(&ftdipp->iq_waiting, timeout) != Q_OK) {
- chSysUnlock();
+ osalSysUnlock();
return r;
}
}
*bp++ = *ftdipp->iq_ptr++;
if (--ftdipp->iq_counter == 0) {
_submitInI(ftdipp);
- chSchRescheduleS();
+ osalOsRescheduleS();
}
- chSysUnlock();
+ osalSysUnlock();
r++;
if (--n == 0U)
return r;
- chSysLock();
+ osalSysLock();
}
}
static msg_t _get_timeout(USBHFTDIPortDriver *ftdipp, systime_t timeout) {
uint8_t b;
- chSysLock();
+ osalSysLock();
if (ftdipp->state != USBHFTDIP_STATE_READY) {
- chSysUnlock();
+ osalSysUnlock();
return Q_RESET;
}
while (ftdipp->iq_counter == 0) {
@@ -578,16 +578,16 @@ static msg_t _get_timeout(USBHFTDIPortDriver *ftdipp, systime_t timeout) {
_submitInI(ftdipp);
msg_t msg = chThdEnqueueTimeoutS(&ftdipp->iq_waiting, timeout);
if (msg < Q_OK) {
- chSysUnlock();
+ osalSysUnlock();
return msg;
}
}
b = *ftdipp->iq_ptr++;
if (--ftdipp->iq_counter == 0) {
_submitInI(ftdipp);
- chSchRescheduleS();
+ osalOsRescheduleS();
}
- chSysUnlock();
+ osalSysUnlock();
return (msg_t)b;
}
@@ -602,7 +602,7 @@ static size_t _read(USBHFTDIPortDriver *ftdipp, uint8_t *bp, size_t n) {
static void _vt(void *p) {
USBHFTDIPortDriver *const ftdipp = (USBHFTDIPortDriver *)p;
- chSysLockFromISR();
+ osalSysLockFromISR();
uint32_t len = ftdipp->oq_ptr - ftdipp->oq_buff;
if (len && !usbhURBIsBusy(&ftdipp->oq_urb)) {
_submitOutI(ftdipp, len);
@@ -610,8 +610,8 @@ static void _vt(void *p) {
if ((ftdipp->iq_counter == 0) && !usbhURBIsBusy(&ftdipp->iq_urb)) {
_submitInI(ftdipp);
}
- chVTSetI(&ftdipp->vt, MS2ST(16), _vt, ftdipp);
- chSysUnlockFromISR();
+ chVTSetI(&ftdipp->vt, OSAL_MS2I(16), _vt, ftdipp);
+ osalSysUnlockFromISR();
}
static const struct FTDIPortDriverVMT async_channel_vmt = {
@@ -626,29 +626,27 @@ static const struct FTDIPortDriverVMT async_channel_vmt = {
};
-static void _stop(USBHFTDIPortDriver *ftdipp) {
- osalSysLock();
+static void _stopS(USBHFTDIPortDriver *ftdipp) {
+ if (ftdipp->state != USBHFTDIP_STATE_READY)
+ return;
chVTResetI(&ftdipp->vt);
usbhEPCloseS(&ftdipp->epin);
usbhEPCloseS(&ftdipp->epout);
chThdDequeueAllI(&ftdipp->iq_waiting, Q_RESET);
chThdDequeueAllI(&ftdipp->oq_waiting, Q_RESET);
- osalOsRescheduleS();
ftdipp->state = USBHFTDIP_STATE_ACTIVE;
- osalSysUnlock();
+ osalOsRescheduleS();
}
void usbhftdipStop(USBHFTDIPortDriver *ftdipp) {
osalDbgCheck((ftdipp->state == USBHFTDIP_STATE_ACTIVE)
|| (ftdipp->state == USBHFTDIP_STATE_READY));
- if (ftdipp->state == USBHFTDIP_STATE_ACTIVE) {
- return;
- }
-
- osalMutexLock(&ftdipp->ftdip->mtx);
- _stop(ftdipp);
- osalMutexUnlock(&ftdipp->ftdip->mtx);
+ osalSysLock();
+ chMtxLockS(&ftdipp->ftdip->mtx);
+ _stopS(ftdipp);
+ chMtxUnlockS(&ftdipp->ftdip->mtx);
+ osalSysUnlock();
}
void usbhftdipStart(USBHFTDIPortDriver *ftdipp, const USBHFTDIPortConfig *config) {
@@ -689,29 +687,37 @@ void usbhftdipStart(USBHFTDIPortDriver *ftdipp, const USBHFTDIPortConfig *config
ftdipp->iq_counter = 0;
ftdipp->iq_ptr = ftdipp->iq_buff;
usbhEPOpen(&ftdipp->epin);
- osalSysLock();
- usbhURBSubmitI(&ftdipp->iq_urb);
- osalSysUnlock();
+ usbhURBSubmit(&ftdipp->iq_urb);
chVTObjectInit(&ftdipp->vt);
- chVTSet(&ftdipp->vt, MS2ST(16), _vt, ftdipp);
+ chVTSet(&ftdipp->vt, OSAL_MS2I(16), _vt, ftdipp);
ftdipp->state = USBHFTDIP_STATE_READY;
osalMutexUnlock(&ftdipp->ftdip->mtx);
}
-void usbhftdiObjectInit(USBHFTDIDriver *ftdip) {
+static void _ftdi_object_init(USBHFTDIDriver *ftdip) {
osalDbgCheck(ftdip != NULL);
memset(ftdip, 0, sizeof(*ftdip));
ftdip->info = &usbhftdiClassDriverInfo;
osalMutexObjectInit(&ftdip->mtx);
}
-void usbhftdipObjectInit(USBHFTDIPortDriver *ftdipp) {
+static void _ftdip_object_init(USBHFTDIPortDriver *ftdipp) {
osalDbgCheck(ftdipp != NULL);
memset(ftdipp, 0, sizeof(*ftdipp));
ftdipp->vmt = &async_channel_vmt;
ftdipp->state = USBHFTDIP_STATE_STOP;
}
+static void _ftdi_init(void) {
+ uint8_t i;
+ for (i = 0; i < HAL_USBHFTDI_MAX_INSTANCES; i++) {
+ _ftdi_object_init(&USBHFTDID[i]);
+ }
+ for (i = 0; i < HAL_USBHFTDI_MAX_PORTS; i++) {
+ _ftdip_object_init(&FTDIPD[i]);
+ }
+}
+
#endif
diff --git a/os/hal/src/usbh/hal_usbh_hid.c b/os/hal/src/usbh/hal_usbh_hid.c
new file mode 100644
index 0000000..2b2c5ce
--- /dev/null
+++ b/os/hal/src/usbh/hal_usbh_hid.c
@@ -0,0 +1,338 @@
+/*
+ ChibiOS - Copyright (C) 2006..2017 Giovanni Di Sirio
+ Copyright (C) 2015..2017 Diego Ismirlian, (dismirlian (at) google's mail)
+
+ Licensed 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.
+*/
+
+#include "hal.h"
+
+#if HAL_USBH_USE_HID
+
+#if !HAL_USE_USBH
+#error "USBHHID needs USBH"
+#endif
+
+#include <string.h>
+#include "usbh/dev/hid.h"
+#include "usbh/internal.h"
+
+#if USBHHID_DEBUG_ENABLE_TRACE
+#define udbgf(f, ...) usbDbgPrintf(f, ##__VA_ARGS__)
+#define udbg(f, ...) usbDbgPuts(f, ##__VA_ARGS__)
+#else
+#define udbgf(f, ...) do {} while(0)
+#define udbg(f, ...) do {} while(0)
+#endif
+
+#if USBHHID_DEBUG_ENABLE_INFO
+#define uinfof(f, ...) usbDbgPrintf(f, ##__VA_ARGS__)
+#define uinfo(f, ...) usbDbgPuts(f, ##__VA_ARGS__)
+#else
+#define uinfof(f, ...) do {} while(0)
+#define uinfo(f, ...) do {} while(0)
+#endif
+
+#if USBHHID_DEBUG_ENABLE_WARNINGS
+#define uwarnf(f, ...) usbDbgPrintf(f, ##__VA_ARGS__)
+#define uwarn(f, ...) usbDbgPuts(f, ##__VA_ARGS__)
+#else
+#define uwarnf(f, ...) do {} while(0)
+#define uwarn(f, ...) do {} while(0)
+#endif
+
+#if USBHHID_DEBUG_ENABLE_ERRORS
+#define uerrf(f, ...) usbDbgPrintf(f, ##__VA_ARGS__)
+#define uerr(f, ...) usbDbgPuts(f, ##__VA_ARGS__)
+#else
+#define uerrf(f, ...) do {} while(0)
+#define uerr(f, ...) do {} while(0)
+#endif
+
+
+
+#define USBH_HID_REQ_GET_REPORT 0x01
+#define USBH_HID_REQ_GET_IDLE 0x02
+#define USBH_HID_REQ_GET_PROTOCOL 0x03
+#define USBH_HID_REQ_SET_REPORT 0x09
+#define USBH_HID_REQ_SET_IDLE 0x0A
+#define USBH_HID_REQ_SET_PROTOCOL 0x0B
+
+/*===========================================================================*/
+/* USB Class driver loader for HID */
+/*===========================================================================*/
+
+USBHHIDDriver USBHHIDD[HAL_USBHHID_MAX_INSTANCES];
+
+static void _hid_init(void);
+static usbh_baseclassdriver_t *_hid_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem);
+static void _hid_unload(usbh_baseclassdriver_t *drv);
+static void _stop_locked(USBHHIDDriver *hidp);
+
+static const usbh_classdriver_vmt_t class_driver_vmt = {
+ _hid_init,
+ _hid_load,
+ _hid_unload
+};
+
+const usbh_classdriverinfo_t usbhhidClassDriverInfo = {
+ "HID", &class_driver_vmt
+};
+
+static usbh_baseclassdriver_t *_hid_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem) {
+ int i;
+ USBHHIDDriver *hidp;
+
+ if (_usbh_match_descriptor(descriptor, rem, USBH_DT_INTERFACE,
+ 0x03, -1, -1) != HAL_SUCCESS)
+ return NULL;
+
+ const usbh_interface_descriptor_t * const ifdesc = (const usbh_interface_descriptor_t *)descriptor;
+
+ if ((ifdesc->bAlternateSetting != 0)
+ || (ifdesc->bNumEndpoints < 1)) {
+ return NULL;
+ }
+
+
+ /* alloc driver */
+ for (i = 0; i < HAL_USBHHID_MAX_INSTANCES; i++) {
+ if (USBHHIDD[i].dev == NULL) {
+ hidp = &USBHHIDD[i];
+ goto alloc_ok;
+ }
+ }
+
+ uwarn("Can't alloc HID driver");
+
+ /* can't alloc */
+ return NULL;
+
+alloc_ok:
+ /* initialize the driver's variables */
+ hidp->epin.status = USBH_EPSTATUS_UNINITIALIZED;
+#if HAL_USBHHID_USE_INTERRUPT_OUT
+ hidp->epout.status = USBH_EPSTATUS_UNINITIALIZED;
+#endif
+ hidp->ifnum = ifdesc->bInterfaceNumber;
+ usbhEPSetName(&dev->ctrl, "HID[CTRL]");
+
+ /* parse the configuration descriptor */
+ if_iterator_t iif;
+ generic_iterator_t iep;
+ iif.iad = 0;
+ iif.curr = descriptor;
+ iif.rem = rem;
+ for (ep_iter_init(&iep, &iif); iep.valid; ep_iter_next(&iep)) {
+ const usbh_endpoint_descriptor_t *const epdesc = ep_get(&iep);
+ if ((epdesc->bEndpointAddress & 0x80) && (epdesc->bmAttributes == USBH_EPTYPE_INT)) {
+ uinfof("INT IN endpoint found: bEndpointAddress=%02x", epdesc->bEndpointAddress);
+ usbhEPObjectInit(&hidp->epin, dev, epdesc);
+ usbhEPSetName(&hidp->epin, "HID[IIN ]");
+#if HAL_USBHHID_USE_INTERRUPT_OUT
+ } else if (((epdesc->bEndpointAddress & 0x80) == 0)
+ && (epdesc->bmAttributes == USBH_EPTYPE_INT)) {
+ uinfof("INT OUT endpoint found: bEndpointAddress=%02x", epdesc->bEndpointAddress);
+ usbhEPObjectInit(&hidp->epout, dev, epdesc);
+ usbhEPSetName(&hidp->epout, "HID[IOUT]");
+#endif
+ } else {
+ uinfof("unsupported endpoint found: bEndpointAddress=%02x, bmAttributes=%02x",
+ epdesc->bEndpointAddress, epdesc->bmAttributes);
+ }
+ }
+ if (hidp->epin.status != USBH_EPSTATUS_CLOSED) {
+ goto deinit;
+ }
+
+ if (ifdesc->bInterfaceSubClass != 0x01) {
+ hidp->type = USBHHID_DEVTYPE_GENERIC;
+ uinfof("HID: bInterfaceSubClass=%02x, generic HID", ifdesc->bInterfaceSubClass);
+ if (ifdesc->bInterfaceSubClass != 0x00) {
+ uinfof("HID: bInterfaceSubClass=%02x is an invalid bInterfaceSubClass value",
+ ifdesc->bInterfaceSubClass);
+ }
+ } else if (ifdesc->bInterfaceProtocol == 0x01) {
+ hidp->type = USBHHID_DEVTYPE_BOOT_KEYBOARD;
+ uinfo("HID: BOOT protocol keyboard found");
+ } else if (ifdesc->bInterfaceProtocol == 0x02) {
+ hidp->type = USBHHID_DEVTYPE_BOOT_MOUSE;
+ uinfo("HID: BOOT protocol mouse found");
+ } else {
+ uerrf("HID: bInterfaceProtocol=%02x is an invalid boot protocol, abort",
+ ifdesc->bInterfaceProtocol);
+ goto deinit;
+ }
+
+ hidp->state = USBHHID_STATE_ACTIVE;
+
+ return (usbh_baseclassdriver_t *)hidp;
+
+deinit:
+ /* Here, the enpoints are closed, and the driver is unlinked */
+ return NULL;
+}
+
+static void _hid_unload(usbh_baseclassdriver_t *drv) {
+ USBHHIDDriver *const hidp = (USBHHIDDriver *)drv;
+ chSemWait(&hidp->sem);
+ _stop_locked(hidp);
+ hidp->state = USBHHID_STATE_STOP;
+ chSemSignal(&hidp->sem);
+}
+
+static void _in_cb(usbh_urb_t *urb) {
+ USBHHIDDriver *const hidp = (USBHHIDDriver *)urb->userData;
+ switch (urb->status) {
+ case USBH_URBSTATUS_OK:
+ if (hidp->config->cb_report) {
+ hidp->config->cb_report(hidp, urb->actualLength);
+ }
+ break;
+ case USBH_URBSTATUS_DISCONNECTED:
+ uwarn("HID: URB IN disconnected");
+
+ return;
+ case USBH_URBSTATUS_TIMEOUT:
+ //no data
+ break;
+ default:
+ uerrf("HID: URB IN status unexpected = %d", urb->status);
+ break;
+ }
+ usbhURBObjectResetI(&hidp->in_urb);
+ usbhURBSubmitI(&hidp->in_urb);
+}
+
+void usbhhidStart(USBHHIDDriver *hidp, const USBHHIDConfig *cfg) {
+ osalDbgCheck(hidp && cfg);
+ osalDbgCheck(cfg->report_buffer && (cfg->protocol <= USBHHID_PROTOCOL_REPORT));
+
+ chSemWait(&hidp->sem);
+ if (hidp->state == USBHHID_STATE_READY) {
+ chSemSignal(&hidp->sem);
+ return;
+ }
+ osalDbgCheck(hidp->state == USBHHID_STATE_ACTIVE);
+
+ hidp->config = cfg;
+
+ /* init the URBs */
+ uint32_t report_len = hidp->epin.wMaxPacketSize;
+ if (report_len > cfg->report_len)
+ report_len = cfg->report_len;
+ usbhURBObjectInit(&hidp->in_urb, &hidp->epin, _in_cb, hidp,
+ cfg->report_buffer, report_len);
+
+ /* open the int IN/OUT endpoints */
+ usbhEPOpen(&hidp->epin);
+#if HAL_USBHHID_USE_INTERRUPT_OUT
+ if (hidp->epout.status == USBH_EPSTATUS_CLOSED) {
+ usbhEPOpen(&hidp->epout);
+ }
+#endif
+
+ usbhhidSetProtocol(hidp, cfg->protocol);
+
+ usbhURBSubmit(&hidp->in_urb);
+
+ hidp->state = USBHHID_STATE_READY;
+ chSemSignal(&hidp->sem);
+}
+
+static void _stop_locked(USBHHIDDriver *hidp) {
+ if (hidp->state == USBHHID_STATE_ACTIVE)
+ return;
+
+ osalDbgCheck(hidp->state == USBHHID_STATE_READY);
+
+ usbhEPClose(&hidp->epin);
+#if HAL_USBHHID_USE_INTERRUPT_OUT
+ if (hidp->epout.status != USBH_EPSTATUS_UNINITIALIZED) {
+ usbhEPClose(&hidp->epout);
+ }
+#endif
+ hidp->state = USBHHID_STATE_ACTIVE;
+}
+
+void usbhhidStop(USBHHIDDriver *hidp) {
+ chSemWait(&hidp->sem);
+ _stop_locked(hidp);
+ chSemSignal(&hidp->sem);
+}
+
+usbh_urbstatus_t usbhhidGetReport(USBHHIDDriver *hidp,
+ uint8_t report_id, usbhhid_reporttype_t report_type,
+ void *data, uint16_t len) {
+ osalDbgCheck(hidp);
+ osalDbgAssert((uint8_t)report_type <= USBHHID_REPORTTYPE_FEATURE, "wrong report type");
+ return usbhControlRequest(hidp->dev,
+ USBH_REQTYPE_CLASSIN(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_GET_REPORT,
+ ((uint8_t)report_type << 8) | report_id, hidp->ifnum, len, data);
+}
+
+usbh_urbstatus_t usbhhidSetReport(USBHHIDDriver *hidp,
+ uint8_t report_id, usbhhid_reporttype_t report_type,
+ const void *data, uint16_t len) {
+ osalDbgCheck(hidp);
+ osalDbgAssert((uint8_t)report_type <= USBHHID_REPORTTYPE_FEATURE, "wrong report type");
+ return usbhControlRequest(hidp->dev,
+ USBH_REQTYPE_CLASSOUT(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_SET_REPORT,
+ ((uint8_t)report_type << 8) | report_id, hidp->ifnum, len, (void *)data);
+}
+
+usbh_urbstatus_t usbhhidGetIdle(USBHHIDDriver *hidp, uint8_t report_id, uint8_t *duration) {
+ osalDbgCheck(hidp);
+ return usbhControlRequest(hidp->dev,
+ USBH_REQTYPE_CLASSIN(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_GET_IDLE,
+ report_id, hidp->ifnum, 1, duration);
+}
+
+usbh_urbstatus_t usbhhidSetIdle(USBHHIDDriver *hidp, uint8_t report_id, uint8_t duration) {
+ osalDbgCheck(hidp);
+ return usbhControlRequest(hidp->dev,
+ USBH_REQTYPE_CLASSOUT(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_SET_IDLE,
+ (duration << 8) | report_id, hidp->ifnum, 0, NULL);
+}
+
+usbh_urbstatus_t usbhhidGetProtocol(USBHHIDDriver *hidp, uint8_t *protocol) {
+ osalDbgCheck(hidp);
+ return usbhControlRequest(hidp->dev,
+ USBH_REQTYPE_CLASSIN(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_GET_PROTOCOL,
+ 0, hidp->ifnum, 1, protocol);
+}
+
+usbh_urbstatus_t usbhhidSetProtocol(USBHHIDDriver *hidp, uint8_t protocol) {
+ osalDbgCheck(hidp);
+ osalDbgAssert(protocol <= 1, "invalid protocol");
+ return usbhControlRequest(hidp->dev,
+ USBH_REQTYPE_CLASSOUT(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_SET_PROTOCOL,
+ protocol, hidp->ifnum, 0, NULL);
+}
+
+static void _hid_object_init(USBHHIDDriver *hidp) {
+ osalDbgCheck(hidp != NULL);
+ memset(hidp, 0, sizeof(*hidp));
+ hidp->info = &usbhhidClassDriverInfo;
+ hidp->state = USBHHID_STATE_STOP;
+ chSemObjectInit(&hidp->sem, 1);
+}
+
+static void _hid_init(void) {
+ uint8_t i;
+ for (i = 0; i < HAL_USBHHID_MAX_INSTANCES; i++) {
+ _hid_object_init(&USBHHIDD[i]);
+ }
+}
+
+#endif
diff --git a/os/hal/src/usbh/hal_usbh_hub.c b/os/hal/src/usbh/hal_usbh_hub.c
index 7fdcef1..6a83c66 100644
--- a/os/hal/src/usbh/hal_usbh_hub.c
+++ b/os/hal/src/usbh/hal_usbh_hub.c
@@ -1,6 +1,6 @@
/*
- ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio
- Copyright (C) 2015 Diego Ismirlian, TISA, (dismirlian (at) google's mail)
+ ChibiOS - Copyright (C) 2006..2017 Giovanni Di Sirio
+ Copyright (C) 2015..2017 Diego Ismirlian, (dismirlian (at) google's mail)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -16,8 +16,6 @@
*/
#include "hal.h"
-#include "hal_usbh.h"
-#include "usbh/internal.h"
#if HAL_USBH_USE_HUB
@@ -27,6 +25,7 @@
#include <string.h>
#include "usbh/dev/hub.h"
+#include "usbh/internal.h"
#if USBHHUB_DEBUG_ENABLE_TRACE
#define udbgf(f, ...) usbDbgPrintf(f, ##__VA_ARGS__)
@@ -62,16 +61,19 @@
USBHHubDriver USBHHUBD[HAL_USBHHUB_MAX_INSTANCES];
-usbh_port_t USBHPorts[HAL_USBHHUB_MAX_PORTS];
+static usbh_port_t USBHPorts[HAL_USBHHUB_MAX_PORTS];
-static usbh_baseclassdriver_t *hub_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem);
-static void hub_unload(usbh_baseclassdriver_t *drv);
+static void _hub_init(void);
+static usbh_baseclassdriver_t *_hub_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem);
+static void _hub_unload(usbh_baseclassdriver_t *drv);
static const usbh_classdriver_vmt_t usbhhubClassDriverVMT = {
- hub_load,
- hub_unload
+ _hub_init,
+ _hub_load,
+ _hub_unload
};
+
const usbh_classdriverinfo_t usbhhubClassDriverInfo = {
- 0x09, 0x00, -1, "HUB", &usbhhubClassDriverVMT
+ "HUB", &usbhhubClassDriverVMT
};
@@ -105,7 +107,7 @@ static void _urb_complete(usbh_urb_t *urb) {
case USBH_URBSTATUS_TIMEOUT:
/* the device NAKed */
udbg("HUB: no info");
- hubdp->statuschange = 0;
+ //hubdp->statuschange = 0;
break;
case USBH_URBSTATUS_OK: {
uint8_t len = hubdp->hubDesc.bNbrPorts / 8 + 1;
@@ -138,16 +140,14 @@ static void _urb_complete(usbh_urb_t *urb) {
usbhURBSubmitI(urb);
}
-static usbh_baseclassdriver_t *hub_load(usbh_device_t *dev,
+static usbh_baseclassdriver_t *_hub_load(usbh_device_t *dev,
const uint8_t *descriptor, uint16_t rem) {
int i;
USBHHubDriver *hubdp;
- if ((rem < descriptor[0]) || (descriptor[1] != USBH_DT_DEVICE))
- return NULL;
-
- if (dev->devDesc.bDeviceProtocol != 0)
+ if (_usbh_match_descriptor(descriptor, rem, USBH_DT_DEVICE,
+ 0x09, 0x00, 0x00) != HAL_SUCCESS)
return NULL;
generic_iterator_t iep, icfg;
@@ -159,12 +159,10 @@ static usbh_baseclassdriver_t *hub_load(usbh_device_t *dev,
if_iter_init(&iif, &icfg);
if (!iif.valid)
return NULL;
- const usbh_interface_descriptor_t *const ifdesc = if_get(&iif);
- if ((ifdesc->bInterfaceClass != 0x09)
- || (ifdesc->bInterfaceSubClass != 0x00)
- || (ifdesc->bInterfaceProtocol != 0x00)) {
+
+ if (_usbh_match_descriptor(iif.curr, iif.rem, USBH_DT_INTERFACE,
+ 0x09, 0x00, 0x00) != HAL_SUCCESS)
return NULL;
- }
ep_iter_init(&iep, &iif);
if (!iep.valid)
@@ -199,7 +197,7 @@ alloc_ok:
/* read Hub descriptor */
uinfo("Read Hub descriptor");
if (usbhhubControlRequest(dev->host, hubdp,
- USBH_REQTYPE_IN | USBH_REQTYPE_CLASS | USBH_REQTYPE_DEVICE,
+ USBH_REQTYPE_DIR_IN | USBH_REQTYPE_TYPE_CLASS | USBH_REQTYPE_RECIP_DEVICE,
USBH_REQ_GET_DESCRIPTOR,
(USBH_DT_HUB << 8), 0, sizeof(hubdp->hubDesc),
(uint8_t *)&hubdp->hubDesc) != USBH_URBSTATUS_OK) {
@@ -254,22 +252,18 @@ alloc_ok:
_urb_complete, hubdp, hubdp->scbuff,
(hubdesc->bNbrPorts + 8) / 8);
- osalSysLock();
- usbhURBSubmitI(&hubdp->urb);
- osalOsRescheduleS();
- osalSysUnlock();
+ usbhURBSubmit(&hubdp->urb);
+ hubdp->dev = NULL;
return (usbh_baseclassdriver_t *)hubdp;
}
-static void hub_unload(usbh_baseclassdriver_t *drv) {
+static void _hub_unload(usbh_baseclassdriver_t *drv) {
osalDbgCheck(drv != NULL);
USBHHubDriver *const hubdp = (USBHHubDriver *)drv;
/* close the status change endpoint (this cancels ongoing URBs) */
- osalSysLock();
- usbhEPCloseS(&hubdp->epint);
- osalSysUnlock();
+ usbhEPClose(&hubdp->epint);
/* de-alloc ports and unload drivers */
usbh_port_t *port = hubdp->ports;
@@ -284,14 +278,23 @@ static void hub_unload(usbh_baseclassdriver_t *drv) {
}
-void usbhhubObjectInit(USBHHubDriver *hubdp) {
+static void _object_init(USBHHubDriver *hubdp) {
osalDbgCheck(hubdp != NULL);
memset(hubdp, 0, sizeof(*hubdp));
hubdp->info = &usbhhubClassDriverInfo;
}
+
+static void _hub_init(void) {
+ uint8_t i;
+ for (i = 0; i < HAL_USBHHUB_MAX_INSTANCES; i++) {
+ _object_init(&USBHHUBD[i]);
+ }
+}
+
#else
#if HAL_USE_USBH
+#include <string.h>
void _usbhub_port_object_init(usbh_port_t *port, USBHDriver *usbh, uint8_t number) {
memset(port, 0, sizeof(*port));
port->number = number;
diff --git a/os/hal/src/usbh/hal_usbh_msd.c b/os/hal/src/usbh/hal_usbh_msd.c
index 6869a74..7233a0b 100644
--- a/os/hal/src/usbh/hal_usbh_msd.c
+++ b/os/hal/src/usbh/hal_usbh_msd.c
@@ -1,6 +1,6 @@
/*
- ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio
- Copyright (C) 2015 Diego Ismirlian, TISA, (dismirlian (at) google's mail)
+ ChibiOS - Copyright (C) 2006..2017 Giovanni Di Sirio
+ Copyright (C) 2015..2017 Diego Ismirlian, (dismirlian (at) google's mail)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
*/
#include "hal.h"
-#include "hal_usbh.h"
#if HAL_USBH_USE_MSD
@@ -28,9 +27,6 @@
#include "usbh/dev/msd.h"
#include "usbh/internal.h"
-//#pragma GCC optimize("Og")
-
-
#if USBHMSD_DEBUG_ENABLE_TRACE
#define udbgf(f, ...) usbDbgPrintf(f, ##__VA_ARGS__)
#define udbg(f, ...) usbDbgPuts(f, ##__VA_ARGS__)
@@ -63,26 +59,39 @@
#define uerr(f, ...) do {} while(0)
#endif
+static void _lun_object_deinit(USBHMassStorageLUNDriver *lunp);
+/*===========================================================================*/
+/* USB Class driver loader for MSD */
+/*===========================================================================*/
+struct USBHMassStorageDriver {
+ /* inherited from abstract class driver */
+ _usbh_base_classdriver_data
+ usbh_ep_t epin;
+ usbh_ep_t epout;
+ uint8_t ifnum;
+ uint8_t max_lun;
+ uint32_t tag;
-/*===========================================================================*/
-/* USB Class driver loader for MSD */
-/*===========================================================================*/
+ USBHMassStorageLUNDriver *luns;
+};
-USBHMassStorageDriver USBHMSD[HAL_USBHMSD_MAX_INSTANCES];
+static USBHMassStorageDriver USBHMSD[HAL_USBHMSD_MAX_INSTANCES];
+static void _msd_init(void);
static usbh_baseclassdriver_t *_msd_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem);
static void _msd_unload(usbh_baseclassdriver_t *drv);
static const usbh_classdriver_vmt_t class_driver_vmt = {
+ _msd_init,
_msd_load,
_msd_unload
};
const usbh_classdriverinfo_t usbhmsdClassDriverInfo = {
- 0x08, 0x06, 0x50, "MSD", &class_driver_vmt
+ "MSD", &class_driver_vmt
};
#define MSD_REQ_RESET 0xFF
@@ -91,18 +100,17 @@ const usbh_classdriverinfo_t usbhmsdClassDriverInfo = {
static usbh_baseclassdriver_t *_msd_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem) {
int i;
USBHMassStorageDriver *msdp;
- uint8_t luns; // should declare it here to eliminate 'control bypass initialization' warning
- usbh_urbstatus_t stat; // should declare it here to eliminate 'control bypass initialization' warning
+ uint8_t luns;
+ usbh_urbstatus_t stat;
- if ((rem < descriptor[0]) || (descriptor[1] != USBH_DT_INTERFACE))
+ if (_usbh_match_descriptor(descriptor, rem, USBH_DT_INTERFACE,
+ 0x08, 0x06, 0x50) != HAL_SUCCESS)
return NULL;
const usbh_interface_descriptor_t * const ifdesc = (const usbh_interface_descriptor_t *)descriptor;
if ((ifdesc->bAlternateSetting != 0)
- || (ifdesc->bNumEndpoints < 2)
- || (ifdesc->bInterfaceSubClass != 0x06)
- || (ifdesc->bInterfaceProtocol != 0x50)) {
+ || (ifdesc->bNumEndpoints < 2)) {
return NULL;
}
@@ -157,10 +165,10 @@ alloc_ok:
/* read the number of LUNs */
uinfo("Reading Max LUN:");
- USBH_DEFINE_BUFFER(uint8_t, buff[4]);
+ USBH_DEFINE_BUFFER(uint8_t buff[4]);
stat = usbhControlRequest(dev,
- USBH_CLASSIN(USBH_REQTYPE_INTERFACE, MSD_GET_MAX_LUN, 0, msdp->ifnum),
- 1, buff);
+ USBH_REQTYPE_CLASSIN(USBH_REQTYPE_RECIP_INTERFACE),
+ MSD_GET_MAX_LUN, 0, msdp->ifnum, 1, buff);
if (stat == USBH_URBSTATUS_OK) {
msdp->max_lun = buff[0] + 1;
uinfof("\tmax_lun = %d", msdp->max_lun);
@@ -188,13 +196,7 @@ alloc_ok:
MSBLKD[i].next = msdp->luns;
msdp->luns = &MSBLKD[i];
MSBLKD[i].msdp = msdp;
-
- osalSysLock();
- MSBLKD[i].state = BLK_ACTIVE; /* transition directly to active, instead of BLK_STOP */
- osalSysUnlock();
-
- /* connect the LUN (TODO: review if it's best to leave the LUN disconnected) */
- usbhmsdLUNConnect(&MSBLKD[i]);
+ MSBLKD[i].state = BLK_ACTIVE;
luns--;
}
}
@@ -211,36 +213,24 @@ static void _msd_unload(usbh_baseclassdriver_t *drv) {
USBHMassStorageDriver *const msdp = (USBHMassStorageDriver *)drv;
USBHMassStorageLUNDriver *lunp = msdp->luns;
- osalMutexLock(&msdp->mtx);
- osalSysLock();
- usbhEPCloseS(&msdp->epin);
- usbhEPCloseS(&msdp->epout);
+ /* disconnect all LUNs */
while (lunp) {
- lunp->state = BLK_STOP;
+ usbhmsdLUNDisconnect(lunp);
+ _lun_object_deinit(lunp);
lunp = lunp->next;
}
- osalSysUnlock();
- osalMutexUnlock(&msdp->mtx);
- /* now that the LUNs are idle, deinit them */
- lunp = msdp->luns;
- osalSysLock();
- while (lunp) {
- usbhmsdLUNObjectInit(lunp);
- lunp = lunp->next;
- }
- osalSysUnlock();
+ usbhEPClose(&msdp->epin);
+ usbhEPClose(&msdp->epout);
}
/*===========================================================================*/
-/* MSD Class driver operations (Bulk-Only transport) */
+/* MSD Class driver operations (Bulk-Only transport) */
/*===========================================================================*/
-
-
/* USB Bulk Only Transport SCSI Command block wrapper */
-PACKED_STRUCT {
+typedef PACKED_STRUCT {
uint32_t dCBWSignature;
uint32_t dCBWTag;
uint32_t dCBWDataTransferLength;
@@ -253,9 +243,8 @@ PACKED_STRUCT {
#define MSD_CBWFLAGS_D2H 0x80
#define MSD_CBWFLAGS_H2D 0x00
-
/* USB Bulk Only Transport SCSI Command status wrapper */
-PACKED_STRUCT {
+typedef PACKED_STRUCT {
uint32_t dCSWSignature;
uint32_t dCSWTag;
uint32_t dCSWDataResidue;
@@ -263,34 +252,174 @@ PACKED_STRUCT {
} msd_csw_t;
#define MSD_CSW_SIGNATURE 0x53425355
-
-typedef union {
- msd_cbw_t cbw;
- msd_csw_t csw;
+typedef struct {
+ msd_cbw_t *cbw;
+ uint8_t csw_status;
+ uint32_t data_processed;
} msd_transaction_t;
typedef enum {
- MSD_TRANSACTIONRESULT_OK,
- MSD_TRANSACTIONRESULT_DISCONNECTED,
- MSD_TRANSACTIONRESULT_STALL,
- MSD_TRANSACTIONRESULT_BUS_ERROR,
- MSD_TRANSACTIONRESULT_SYNC_ERROR
-} msd_transaction_result_t;
+ MSD_BOTRESULT_OK,
+ MSD_BOTRESULT_DISCONNECTED,
+ MSD_BOTRESULT_ERROR
+} msd_bot_result_t;
typedef enum {
- MSD_COMMANDRESULT_PASSED = 0,
- MSD_COMMANDRESULT_FAILED = 1,
- MSD_COMMANDRESULT_PHASE_ERROR = 2
-} msd_command_result_t;
-
-typedef struct {
- msd_transaction_result_t tres;
- msd_command_result_t cres;
+ MSD_RESULT_OK = MSD_BOTRESULT_OK,
+ MSD_RESULT_DISCONNECTED = MSD_BOTRESULT_DISCONNECTED,
+ MSD_RESULT_TRANSPORT_ERROR = MSD_BOTRESULT_ERROR,
+ MSD_RESULT_FAILED
} msd_result_t;
+#define CSW_STATUS_PASSED 0
+#define CSW_STATUS_FAILED 1
+#define CSW_STATUS_PHASE_ERROR 2
+
+static bool _msd_bot_reset(USBHMassStorageDriver *msdp) {
+
+ usbh_urbstatus_t res;
+ res = usbhControlRequest(msdp->dev,
+ USBH_REQTYPE_CLASSOUT(USBH_REQTYPE_RECIP_INTERFACE),
+ 0xFF, 0, msdp->ifnum, 0, NULL);
+ if (res != USBH_URBSTATUS_OK) {
+ return FALSE;
+ }
+
+ osalThreadSleepMilliseconds(100);
+
+ return usbhEPReset(&msdp->epin) && usbhEPReset(&msdp->epout);
+}
+
+static msd_bot_result_t _msd_bot_transaction(msd_transaction_t *tran, USBHMassStorageLUNDriver *lunp, void *data) {
+
+ uint32_t data_actual_len, actual_len;
+ usbh_urbstatus_t status;
+ USBH_DEFINE_BUFFER(msd_csw_t csw);
+
+ tran->cbw->bCBWLUN = (uint8_t)(lunp - &lunp->msdp->luns[0]);
+ tran->cbw->dCBWSignature = MSD_CBW_SIGNATURE;
+ tran->cbw->dCBWTag = ++lunp->msdp->tag;
+ tran->data_processed = 0;
+
+ /* control phase */
+ status = usbhBulkTransfer(&lunp->msdp->epout, tran->cbw,
+ sizeof(*tran->cbw), &actual_len, OSAL_MS2I(1000));
+
+ if (status == USBH_URBSTATUS_CANCELLED) {
+ uerr("\tMSD: Control phase: USBH_URBSTATUS_CANCELLED");
+ return MSD_BOTRESULT_DISCONNECTED;
+ }
+
+ if ((status != USBH_URBSTATUS_OK) || (actual_len != sizeof(*tran->cbw))) {
+ uerrf("\tMSD: Control phase: status = %d (!= OK), actual_len = %d (expected to send %d)",
+ status, actual_len, sizeof(*tran->cbw));
+ _msd_bot_reset(lunp->msdp);
+ return MSD_BOTRESULT_ERROR;
+ }
+
+
+ /* data phase */
+ data_actual_len = 0;
+ if (tran->cbw->dCBWDataTransferLength) {
+ usbh_ep_t *const ep = tran->cbw->bmCBWFlags & MSD_CBWFLAGS_D2H ? &lunp->msdp->epin : &lunp->msdp->epout;
+ status = usbhBulkTransfer(
+ ep,
+ data,
+ tran->cbw->dCBWDataTransferLength,
+ &data_actual_len, OSAL_MS2I(20000));
+
+ if (status == USBH_URBSTATUS_CANCELLED) {
+ uerr("\tMSD: Data phase: USBH_URBSTATUS_CANCELLED");
+ return MSD_BOTRESULT_DISCONNECTED;
+ }
+
+ if (status == USBH_URBSTATUS_STALL) {
+ uerrf("\tMSD: Data phase: USBH_URBSTATUS_STALL, clear halt");
+ status = (usbhEPReset(ep) == HAL_SUCCESS) ? USBH_URBSTATUS_OK : USBH_URBSTATUS_ERROR;
+ }
+
+ if (status != USBH_URBSTATUS_OK) {
+ uerrf("\tMSD: Data phase: status = %d (!= OK), resetting", status);
+ _msd_bot_reset(lunp->msdp);
+ return MSD_BOTRESULT_ERROR;
+ }
+ }
+
+
+ /* status phase */
+ status = usbhBulkTransfer(&lunp->msdp->epin, &csw,
+ sizeof(csw), &actual_len, OSAL_MS2I(1000));
+
+ if (status == USBH_URBSTATUS_STALL) {
+ uwarn("\tMSD: Status phase: USBH_URBSTATUS_STALL, clear halt and retry");
+
+ status = (usbhEPReset(&lunp->msdp->epin) == HAL_SUCCESS) ? USBH_URBSTATUS_OK : USBH_URBSTATUS_ERROR;
+
+ if (status == USBH_URBSTATUS_OK) {
+ status = usbhBulkTransfer(&lunp->msdp->epin, &csw,
+ sizeof(csw), &actual_len, OSAL_MS2I(1000));
+ }
+ }
+
+ if (status == USBH_URBSTATUS_CANCELLED) {
+ uerr("\tMSD: Status phase: USBH_URBSTATUS_CANCELLED");
+ return MSD_BOTRESULT_DISCONNECTED;
+ }
+
+ if (status != USBH_URBSTATUS_OK) {
+ uerrf("\tMSD: Status phase: status = %d (!= OK), resetting", status);
+ _msd_bot_reset(lunp->msdp);
+ return MSD_BOTRESULT_ERROR;
+ }
+
+ /* validate CSW */
+ if ((actual_len != sizeof(csw))
+ || (csw.dCSWSignature != MSD_CSW_SIGNATURE)
+ || (csw.dCSWTag != lunp->msdp->tag)
+ || (csw.bCSWStatus >= CSW_STATUS_PHASE_ERROR)) {
+ /* CSW is not valid */
+ uerrf("\tMSD: Status phase: Invalid CSW: len=%d, dCSWSignature=%x, dCSWTag=%x (expected %x), bCSWStatus=%d, resetting",
+ actual_len,
+ csw.dCSWSignature,
+ csw.dCSWTag,
+ lunp->msdp->tag,
+ csw.bCSWStatus);
+ _msd_bot_reset(lunp->msdp);
+ return MSD_BOTRESULT_ERROR;
+ }
+
+ /* check if CSW is meaningful */
+ if ((csw.bCSWStatus != CSW_STATUS_PHASE_ERROR)
+ && (csw.dCSWDataResidue > tran->cbw->dCBWDataTransferLength)) {
+ /* CSW is not meaningful */
+ uerrf("\tMSD: Status phase: CSW not meaningful: bCSWStatus=%d, dCSWDataResidue=%u, dCBWDataTransferLength=%u, resetting",
+ csw.bCSWStatus,
+ csw.dCSWDataResidue,
+ tran->cbw->dCBWDataTransferLength);
+ _msd_bot_reset(lunp->msdp);
+ return MSD_BOTRESULT_ERROR;
+ }
+
+ if (csw.bCSWStatus == CSW_STATUS_PHASE_ERROR) {
+ uerr("\tMSD: Status phase: Phase error, resetting");
+ _msd_bot_reset(lunp->msdp);
+ return MSD_BOTRESULT_ERROR;
+ }
+
+ tran->data_processed = tran->cbw->dCBWDataTransferLength - csw.dCSWDataResidue;
+ if (data_actual_len < tran->data_processed) {
+ tran->data_processed = data_actual_len;
+ }
+
+ tran->csw_status = csw.bCSWStatus;
+
+ return MSD_BOTRESULT_OK;
+}
+
+
/* ----------------------------------------------------- */
-/* SCSI Commands */
+/* SCSI Commands */
/* ----------------------------------------------------- */
/* Read 10 and Write 10 */
@@ -299,7 +428,7 @@ typedef struct {
/* Request sense */
#define SCSI_CMD_REQUEST_SENSE 0x03
-PACKED_STRUCT {
+typedef PACKED_STRUCT {
uint8_t byte[18];
} scsi_sense_response_t;
@@ -333,7 +462,7 @@ PACKED_STRUCT {
/* Inquiry */
#define SCSI_CMD_INQUIRY 0x12
-PACKED_STRUCT {
+typedef PACKED_STRUCT {
uint8_t peripheral;
uint8_t removable;
uint8_t version;
@@ -349,14 +478,14 @@ PACKED_STRUCT {
/* Read Capacity 10 */
#define SCSI_CMD_READ_CAPACITY_10 0x25
-PACKED_STRUCT {
+typedef PACKED_STRUCT {
uint32_t last_block_addr;
uint32_t block_size;
} scsi_readcapacity10_response_t;
/* Start/Stop Unit */
#define SCSI_CMD_START_STOP_UNIT 0x1B
-PACKED_STRUCT {
+typedef PACKED_STRUCT {
uint8_t op_code;
uint8_t lun_immed;
uint8_t res1;
@@ -368,223 +497,187 @@ PACKED_STRUCT {
/* test unit ready */
#define SCSI_CMD_TEST_UNIT_READY 0x00
-/* Other commands, TODO: use or remove them
-#define SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1E
-#define SCSI_CMD_VERIFY_10 0x2F
-#define SCSI_CMD_SEND_DIAGNOSTIC 0x1D
-#define SCSI_CMD_MODE_SENSE_6 0x1A
-*/
+static msd_result_t scsi_requestsense(USBHMassStorageLUNDriver *lunp, scsi_sense_response_t *resp);
-static inline void _prepare_cbw(msd_transaction_t *tran, USBHMassStorageLUNDriver *lunp) {
- tran->cbw.bCBWLUN = (uint8_t)(lunp - &lunp->msdp->luns[0]);
- memset(&tran->cbw.CBWCB, 0, sizeof(tran->cbw.CBWCB));
-}
-
-static msd_transaction_result_t _msd_transaction(msd_transaction_t *tran, USBHMassStorageLUNDriver *lunp, void *data) {
+static msd_result_t _scsi_perform_transaction(USBHMassStorageLUNDriver *lunp,
+ msd_transaction_t *transaction, void *data) {
- uint32_t actual_len;
- usbh_urbstatus_t status;
-
- tran->cbw.dCBWSignature = MSD_CBW_SIGNATURE;
- tran->cbw.dCBWTag = ++lunp->msdp->tag;
-
- /* control phase */
- status = usbhBulkTransfer(&lunp->msdp->epout, &tran->cbw,
- sizeof(tran->cbw), &actual_len, MS2ST(1000));
-
- if (status == USBH_URBSTATUS_CANCELLED) {
- uerr("\tMSD: Control phase: USBH_URBSTATUS_CANCELLED");
- return MSD_TRANSACTIONRESULT_DISCONNECTED;
- } else if (status == USBH_URBSTATUS_STALL) {
- uerr("\tMSD: Control phase: USBH_URBSTATUS_STALL");
- return MSD_TRANSACTIONRESULT_STALL;
- } else if (status != USBH_URBSTATUS_OK) {
- uerrf("\tMSD: Control phase: status = %d, != OK", status);
- return MSD_TRANSACTIONRESULT_BUS_ERROR;
- } else if (actual_len != sizeof(tran->cbw)) {
- uerrf("\tMSD: Control phase: wrong actual_len = %d", actual_len);
- return MSD_TRANSACTIONRESULT_BUS_ERROR;
+ msd_bot_result_t res;
+ res = _msd_bot_transaction(transaction, lunp, data);
+ if (res != MSD_BOTRESULT_OK) {
+ return (msd_result_t)res;
}
-
- /* data phase */
- if (tran->cbw.dCBWDataTransferLength) {
- status = usbhBulkTransfer(
- tran->cbw.bmCBWFlags & MSD_CBWFLAGS_D2H ? &lunp->msdp->epin : &lunp->msdp->epout,
- data,
- tran->cbw.dCBWDataTransferLength,
- &actual_len, MS2ST(20000));
-
- if (status == USBH_URBSTATUS_CANCELLED) {
- uerr("\tMSD: Data phase: USBH_URBSTATUS_CANCELLED");
- return MSD_TRANSACTIONRESULT_DISCONNECTED;
- } else if (status == USBH_URBSTATUS_STALL) {
- uerr("\tMSD: Data phase: USBH_URBSTATUS_STALL");
- return MSD_TRANSACTIONRESULT_STALL;
- } else if (status != USBH_URBSTATUS_OK) {
- uerrf("\tMSD: Data phase: status = %d, != OK", status);
- return MSD_TRANSACTIONRESULT_BUS_ERROR;
- } else if (actual_len != tran->cbw.dCBWDataTransferLength) {
- uerrf("\tMSD: Data phase: wrong actual_len = %d", actual_len);
- return MSD_TRANSACTIONRESULT_BUS_ERROR;
+ if (transaction->csw_status == CSW_STATUS_FAILED) {
+ if (transaction->cbw->CBWCB[0] != SCSI_CMD_REQUEST_SENSE) {
+ /* do auto-sense (except for SCSI_CMD_REQUEST_SENSE!) */
+ uwarn("\tMSD: Command failed, auto-sense");
+ USBH_DEFINE_BUFFER(scsi_sense_response_t sense);
+ if (scsi_requestsense(lunp, &sense) == MSD_RESULT_OK) {
+ uwarnf("\tMSD: REQUEST SENSE: Sense key=%x, ASC=%02x, ASCQ=%02x",
+ sense.byte[2] & 0xf, sense.byte[12], sense.byte[13]);
+ }
}
+ return MSD_RESULT_FAILED;
}
-
- /* status phase */
- status = usbhBulkTransfer(&lunp->msdp->epin, &tran->csw,
- sizeof(tran->csw), &actual_len, MS2ST(1000));
-
- if (status == USBH_URBSTATUS_CANCELLED) {
- uerr("\tMSD: Status phase: USBH_URBSTATUS_CANCELLED");
- return MSD_TRANSACTIONRESULT_DISCONNECTED;
- } else if (status == USBH_URBSTATUS_STALL) {
- uerr("\tMSD: Status phase: USBH_URBSTATUS_STALL");
- return MSD_TRANSACTIONRESULT_STALL;
- } else if (status != USBH_URBSTATUS_OK) {
- uerrf("\tMSD: Status phase: status = %d, != OK", status);
- return MSD_TRANSACTIONRESULT_BUS_ERROR;
- } else if (actual_len != sizeof(tran->csw)) {
- uerrf("\tMSD: Status phase: wrong actual_len = %d", actual_len);
- return MSD_TRANSACTIONRESULT_BUS_ERROR;
- } else if (tran->csw.dCSWSignature != MSD_CSW_SIGNATURE) {
- uerr("\tMSD: Status phase: wrong signature");
- return MSD_TRANSACTIONRESULT_BUS_ERROR;
- } else if (tran->csw.dCSWTag != lunp->msdp->tag) {
- uerrf("\tMSD: Status phase: wrong tag (expected %d, got %d)",
- lunp->msdp->tag, tran->csw.dCSWTag);
- return MSD_TRANSACTIONRESULT_SYNC_ERROR;
- }
-
- if (tran->csw.dCSWDataResidue) {
- uwarnf("\tMSD: Residue=%d", tran->csw.dCSWDataResidue);
- }
-
- return MSD_TRANSACTIONRESULT_OK;
+ return MSD_RESULT_OK;
}
-
static msd_result_t scsi_inquiry(USBHMassStorageLUNDriver *lunp, scsi_inquiry_response_t *resp) {
+ USBH_DEFINE_BUFFER(msd_cbw_t cbw);
msd_transaction_t transaction;
msd_result_t res;
- _prepare_cbw(&transaction, lunp);
- transaction.cbw.dCBWDataTransferLength = sizeof(scsi_inquiry_response_t);
- transaction.cbw.bmCBWFlags = MSD_CBWFLAGS_D2H;
- transaction.cbw.bCBWCBLength = 6;
- transaction.cbw.CBWCB[0] = SCSI_CMD_INQUIRY;
- transaction.cbw.CBWCB[4] = sizeof(scsi_inquiry_response_t);
-
- res.tres = _msd_transaction(&transaction, lunp, resp);
- if (res.tres == MSD_TRANSACTIONRESULT_OK) {
- res.cres = (msd_command_result_t) transaction.csw.bCSWStatus;
+ memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB));
+ cbw.dCBWDataTransferLength = sizeof(scsi_inquiry_response_t);
+ cbw.bmCBWFlags = MSD_CBWFLAGS_D2H;
+ cbw.bCBWCBLength = 6;
+ cbw.CBWCB[0] = SCSI_CMD_INQUIRY;
+ cbw.CBWCB[4] = sizeof(scsi_inquiry_response_t);
+ transaction.cbw = &cbw;
+
+ res = _scsi_perform_transaction(lunp, &transaction, resp);
+ if (res == MSD_RESULT_OK) {
+ //transaction is OK; check length
+ if (transaction.data_processed < cbw.dCBWDataTransferLength) {
+ res = MSD_RESULT_TRANSPORT_ERROR;
+ }
}
+
return res;
}
static msd_result_t scsi_requestsense(USBHMassStorageLUNDriver *lunp, scsi_sense_response_t *resp) {
+ USBH_DEFINE_BUFFER(msd_cbw_t cbw);
msd_transaction_t transaction;
msd_result_t res;
- _prepare_cbw(&transaction, lunp);
- transaction.cbw.dCBWDataTransferLength = sizeof(scsi_sense_response_t);
- transaction.cbw.bmCBWFlags = MSD_CBWFLAGS_D2H;
- transaction.cbw.bCBWCBLength = 12;
- transaction.cbw.CBWCB[0] = SCSI_CMD_REQUEST_SENSE;
- transaction.cbw.CBWCB[4] = sizeof(scsi_sense_response_t);
-
- res.tres = _msd_transaction(&transaction, lunp, resp);
- if (res.tres == MSD_TRANSACTIONRESULT_OK) {
- res.cres = (msd_command_result_t) transaction.csw.bCSWStatus;
+ memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB));
+ cbw.dCBWDataTransferLength = sizeof(scsi_sense_response_t);
+ cbw.bmCBWFlags = MSD_CBWFLAGS_D2H;
+ cbw.bCBWCBLength = 12;
+ cbw.CBWCB[0] = SCSI_CMD_REQUEST_SENSE;
+ cbw.CBWCB[4] = sizeof(scsi_sense_response_t);
+ transaction.cbw = &cbw;
+
+ res = _scsi_perform_transaction(lunp, &transaction, resp);
+ if (res == MSD_RESULT_OK) {
+ //transaction is OK; check length
+ if (transaction.data_processed < cbw.dCBWDataTransferLength) {
+ res = MSD_RESULT_TRANSPORT_ERROR;
+ }
}
+
return res;
}
static msd_result_t scsi_testunitready(USBHMassStorageLUNDriver *lunp) {
+ USBH_DEFINE_BUFFER(msd_cbw_t cbw);
msd_transaction_t transaction;
- msd_result_t res;
- _prepare_cbw(&transaction, lunp);
- transaction.cbw.dCBWDataTransferLength = 0;
- transaction.cbw.bmCBWFlags = MSD_CBWFLAGS_D2H;
- transaction.cbw.bCBWCBLength = 6;
- transaction.cbw.CBWCB[0] = SCSI_CMD_TEST_UNIT_READY;
+ memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB));
+ cbw.dCBWDataTransferLength = 0;
+ cbw.bmCBWFlags = MSD_CBWFLAGS_D2H;
+ cbw.bCBWCBLength = 6;
+ cbw.CBWCB[0] = SCSI_CMD_TEST_UNIT_READY;
+ transaction.cbw = &cbw;
- res.tres = _msd_transaction(&transaction, lunp, NULL);
- if (res.tres == MSD_TRANSACTIONRESULT_OK) {
- res.cres = (msd_command_result_t) transaction.csw.bCSWStatus;
- }
- return res;
+ return _scsi_perform_transaction(lunp, &transaction, NULL);
}
static msd_result_t scsi_readcapacity10(USBHMassStorageLUNDriver *lunp, scsi_readcapacity10_response_t *resp) {
+ USBH_DEFINE_BUFFER(msd_cbw_t cbw);
msd_transaction_t transaction;
msd_result_t res;
- _prepare_cbw(&transaction, lunp);
- transaction.cbw.dCBWDataTransferLength = sizeof(scsi_readcapacity10_response_t);
- transaction.cbw.bmCBWFlags = MSD_CBWFLAGS_D2H;
- transaction.cbw.bCBWCBLength = 12;
- transaction.cbw.CBWCB[0] = SCSI_CMD_READ_CAPACITY_10;
-
- res.tres = _msd_transaction(&transaction, lunp, resp);
- if (res.tres == MSD_TRANSACTIONRESULT_OK) {
- res.cres = (msd_command_result_t) transaction.csw.bCSWStatus;
+ memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB));
+ cbw.dCBWDataTransferLength = sizeof(scsi_readcapacity10_response_t);
+ cbw.bmCBWFlags = MSD_CBWFLAGS_D2H;
+ cbw.bCBWCBLength = 12;
+ cbw.CBWCB[0] = SCSI_CMD_READ_CAPACITY_10;
+ transaction.cbw = &cbw;
+
+ res = _scsi_perform_transaction(lunp, &transaction, resp);
+ if (res == MSD_RESULT_OK) {
+ //transaction is OK; check length
+ if (transaction.data_processed < cbw.dCBWDataTransferLength) {
+ res = MSD_RESULT_TRANSPORT_ERROR;
+ }
}
+
return res;
}
-static msd_result_t scsi_read10(USBHMassStorageLUNDriver *lunp, uint32_t lba, uint16_t n, uint8_t *data) {
+static msd_result_t scsi_read10(USBHMassStorageLUNDriver *lunp, uint32_t lba, uint16_t n, uint8_t *data, uint32_t *actual_len) {
+ USBH_DEFINE_BUFFER(msd_cbw_t cbw);
msd_transaction_t transaction;
msd_result_t res;
- _prepare_cbw(&transaction, lunp);
- transaction.cbw.dCBWDataTransferLength = n * lunp->info.blk_size;
- transaction.cbw.bmCBWFlags = MSD_CBWFLAGS_D2H;
- transaction.cbw.bCBWCBLength = 10;
- transaction.cbw.CBWCB[0] = SCSI_CMD_READ_10;
- transaction.cbw.CBWCB[2] = (uint8_t)(lba >> 24);
- transaction.cbw.CBWCB[3] = (uint8_t)(lba >> 16);
- transaction.cbw.CBWCB[4] = (uint8_t)(lba >> 8);
- transaction.cbw.CBWCB[5] = (uint8_t)(lba);
- transaction.cbw.CBWCB[7] = (uint8_t)(n >> 8);
- transaction.cbw.CBWCB[8] = (uint8_t)(n);
-
- res.tres = _msd_transaction(&transaction, lunp, data);
- if (res.tres == MSD_TRANSACTIONRESULT_OK) {
- res.cres = (msd_command_result_t) transaction.csw.bCSWStatus;
+ memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB));
+ cbw.dCBWDataTransferLength = n * lunp->info.blk_size;
+ cbw.bmCBWFlags = MSD_CBWFLAGS_D2H;
+ cbw.bCBWCBLength = 10;
+ cbw.CBWCB[0] = SCSI_CMD_READ_10;
+ cbw.CBWCB[2] = (uint8_t)(lba >> 24);
+ cbw.CBWCB[3] = (uint8_t)(lba >> 16);
+ cbw.CBWCB[4] = (uint8_t)(lba >> 8);
+ cbw.CBWCB[5] = (uint8_t)(lba);
+ cbw.CBWCB[7] = (uint8_t)(n >> 8);
+ cbw.CBWCB[8] = (uint8_t)(n);
+ transaction.cbw = &cbw;
+
+ res = _scsi_perform_transaction(lunp, &transaction, data);
+ if (actual_len) {
+ *actual_len = transaction.data_processed;
}
+ if (res == MSD_RESULT_OK) {
+ //transaction is OK; check length
+ if (transaction.data_processed < cbw.dCBWDataTransferLength) {
+ res = MSD_RESULT_TRANSPORT_ERROR;
+ }
+ }
+
return res;
}
-static msd_result_t scsi_write10(USBHMassStorageLUNDriver *lunp, uint32_t lba, uint16_t n, const uint8_t *data) {
+static msd_result_t scsi_write10(USBHMassStorageLUNDriver *lunp, uint32_t lba, uint16_t n, const uint8_t *data, uint32_t *actual_len) {
+ USBH_DEFINE_BUFFER(msd_cbw_t cbw);
msd_transaction_t transaction;
msd_result_t res;
- _prepare_cbw(&transaction, lunp);
- transaction.cbw.dCBWDataTransferLength = n * lunp->info.blk_size;
- transaction.cbw.bmCBWFlags = MSD_CBWFLAGS_H2D;
- transaction.cbw.bCBWCBLength = 10;
- transaction.cbw.CBWCB[0] = SCSI_CMD_WRITE_10;
- transaction.cbw.CBWCB[2] = (uint8_t)(lba >> 24);
- transaction.cbw.CBWCB[3] = (uint8_t)(lba >> 16);
- transaction.cbw.CBWCB[4] = (uint8_t)(lba >> 8);
- transaction.cbw.CBWCB[5] = (uint8_t)(lba);
- transaction.cbw.CBWCB[7] = (uint8_t)(n >> 8);
- transaction.cbw.CBWCB[8] = (uint8_t)(n);
-
- res.tres = _msd_transaction(&transaction, lunp, (uint8_t *)data);
- if (res.tres == MSD_TRANSACTIONRESULT_OK) {
- res.cres = (msd_command_result_t) transaction.csw.bCSWStatus;
+ memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB));
+ cbw.dCBWDataTransferLength = n * lunp->info.blk_size;
+ cbw.bmCBWFlags = MSD_CBWFLAGS_H2D;
+ cbw.bCBWCBLength = 10;
+ cbw.CBWCB[0] = SCSI_CMD_WRITE_10;
+ cbw.CBWCB[2] = (uint8_t)(lba >> 24);
+ cbw.CBWCB[3] = (uint8_t)(lba >> 16);
+ cbw.CBWCB[4] = (uint8_t)(lba >> 8);
+ cbw.CBWCB[5] = (uint8_t)(lba);
+ cbw.CBWCB[7] = (uint8_t)(n >> 8);
+ cbw.CBWCB[8] = (uint8_t)(n);
+ transaction.cbw = &cbw;
+
+ res = _scsi_perform_transaction(lunp, &transaction, (void *)data);
+ if (actual_len) {
+ *actual_len = transaction.data_processed;
+ }
+ if (res == MSD_RESULT_OK) {
+ //transaction is OK; check length
+ if (transaction.data_processed < cbw.dCBWDataTransferLength) {
+ res = MSD_RESULT_TRANSPORT_ERROR;
+ }
}
+
return res;
}
/*===========================================================================*/
-/* Block driver data/functions */
+/* Block driver data/functions */
/*===========================================================================*/
USBHMassStorageLUNDriver MSBLKD[HAL_USBHMSD_MAX_LUNS];
@@ -600,39 +693,22 @@ static const struct USBHMassStorageDriverVMT blk_vmt = {
(bool (*)(void *, BlockDeviceInfo *))usbhmsdLUNGetInfo
};
-
-
-static uint32_t _requestsense(USBHMassStorageLUNDriver *lunp) {
- scsi_sense_response_t sense;
- msd_result_t res;
-
- res = scsi_requestsense(lunp, &sense);
- if (res.tres != MSD_TRANSACTIONRESULT_OK) {
- uerr("\tREQUEST SENSE: Transaction error");
- goto failed;
- } else if (res.cres == MSD_COMMANDRESULT_FAILED) {
- uerr("\tREQUEST SENSE: Command Failed");
- goto failed;
- } else if (res.cres == MSD_COMMANDRESULT_PHASE_ERROR) {
- //TODO: Do reset, etc.
- uerr("\tREQUEST SENSE: Command Phase Error");
- goto failed;
- }
-
- uerrf("\tREQUEST SENSE: Sense key=%x, ASC=%02x, ASCQ=%02x",
- sense.byte[2] & 0xf, sense.byte[12], sense.byte[13]);
-
- return (sense.byte[2] & 0xf) | (sense.byte[12] << 8) | (sense.byte[13] << 16);
-
-failed:
- return 0xffffffff;
+static void _lun_object_deinit(USBHMassStorageLUNDriver *lunp) {
+ osalDbgCheck(lunp != NULL);
+ chSemWait(&lunp->sem);
+ lunp->msdp = NULL;
+ lunp->next = NULL;
+ memset(&lunp->info, 0, sizeof(lunp->info));
+ lunp->state = BLK_STOP;
+ chSemSignal(&lunp->sem);
}
-void usbhmsdLUNObjectInit(USBHMassStorageLUNDriver *lunp) {
+static void _lun_object_init(USBHMassStorageLUNDriver *lunp) {
osalDbgCheck(lunp != NULL);
memset(lunp, 0, sizeof(*lunp));
lunp->vmt = &blk_vmt;
lunp->state = BLK_STOP;
+ chSemObjectInit(&lunp->sem, 1);
/* Unnecessary because of the memset:
lunp->msdp = NULL;
lunp->next = NULL;
@@ -640,150 +716,111 @@ void usbhmsdLUNObjectInit(USBHMassStorageLUNDriver *lunp) {
*/
}
-void usbhmsdLUNStart(USBHMassStorageLUNDriver *lunp) {
- osalDbgCheck(lunp != NULL);
- osalSysLock();
- osalDbgAssert((lunp->state == BLK_STOP) || (lunp->state == BLK_ACTIVE),
- "invalid state");
- //TODO: complete
- //lunp->state = BLK_ACTIVE;
- osalSysUnlock();
-}
-
-void usbhmsdLUNStop(USBHMassStorageLUNDriver *lunp) {
- osalDbgCheck(lunp != NULL);
- osalSysLock();
- osalDbgAssert((lunp->state == BLK_STOP) || (lunp->state == BLK_ACTIVE),
- "invalid state");
- //TODO: complete
- //lunp->state = BLK_STOP;
- osalSysUnlock();
-}
-
bool usbhmsdLUNConnect(USBHMassStorageLUNDriver *lunp) {
- USBHMassStorageDriver *const msdp = lunp->msdp;
+ osalDbgCheck(lunp != NULL);
+ osalDbgCheck(lunp->msdp != NULL);
msd_result_t res;
- osalDbgCheck(msdp != NULL);
- osalSysLock();
- //osalDbgAssert((lunp->state == BLK_ACTIVE) || (lunp->state == BLK_READY),
- // "invalid state");
+ chSemWait(&lunp->sem);
+ osalDbgAssert((lunp->state == BLK_READY) || (lunp->state == BLK_ACTIVE), "invalid state");
if (lunp->state == BLK_READY) {
- osalSysUnlock();
+ chSemSignal(&lunp->sem);
return HAL_SUCCESS;
- } else if (lunp->state != BLK_ACTIVE) {
- osalSysUnlock();
- return HAL_FAILED;
}
lunp->state = BLK_CONNECTING;
- osalSysUnlock();
- osalMutexLock(&msdp->mtx);
-
- USBH_DEFINE_BUFFER(union {
- scsi_inquiry_response_t inq;
- scsi_readcapacity10_response_t cap; }, u);
-
- uinfo("INQUIRY...");
- res = scsi_inquiry(lunp, &u.inq);
- if (res.tres != MSD_TRANSACTIONRESULT_OK) {
- uerr("\tINQUIRY: Transaction error");
- goto failed;
- } else if (res.cres == MSD_COMMANDRESULT_FAILED) {
- uerr("\tINQUIRY: Command Failed");
- _requestsense(lunp);
- goto failed;
- } else if (res.cres == MSD_COMMANDRESULT_PHASE_ERROR) {
- //TODO: Do reset, etc.
- uerr("\tINQUIRY: Command Phase Error");
- goto failed;
- }
+ {
+ USBH_DEFINE_BUFFER(scsi_inquiry_response_t inq);
+ uinfo("INQUIRY...");
+ res = scsi_inquiry(lunp, &inq);
+ if (res == MSD_RESULT_DISCONNECTED) {
+ goto failed;
+ } else if (res == MSD_RESULT_TRANSPORT_ERROR) {
+ //retry?
+ goto failed;
+ } else if (res == MSD_RESULT_FAILED) {
+ //retry?
+ goto failed;
+ }
- uinfof("\tPDT=%02x", u.inq.peripheral & 0x1f);
- if (u.inq.peripheral != 0) {
- uerr("\tUnsupported PDT");
- goto failed;
+ uinfof("\tPDT=%02x", inq.peripheral & 0x1f);
+ if (inq.peripheral != 0) {
+ uerr("\tUnsupported PDT");
+ goto failed;
+ }
}
// Test if unit ready
- uint8_t i;
+ uint8_t i;
for (i = 0; i < 10; i++) {
uinfo("TEST UNIT READY...");
res = scsi_testunitready(lunp);
- if (res.tres != MSD_TRANSACTIONRESULT_OK) {
- uerr("\tTEST UNIT READY: Transaction error");
+ if (res == MSD_RESULT_DISCONNECTED) {
goto failed;
- } else if (res.cres == MSD_COMMANDRESULT_FAILED) {
- uerr("\tTEST UNIT READY: Command Failed");
- _requestsense(lunp);
- continue;
- } else if (res.cres == MSD_COMMANDRESULT_PHASE_ERROR) {
- //TODO: Do reset, etc.
- uerr("\tTEST UNIT READY: Command Phase Error");
+ } else if (res == MSD_RESULT_TRANSPORT_ERROR) {
+ //retry?
goto failed;
+ } else if (res == MSD_RESULT_FAILED) {
+ uinfo("\tTEST UNIT READY: Command Failed, retry");
+ osalThreadSleepMilliseconds(200);
+ continue;
}
uinfo("\tReady.");
break;
- // osalThreadSleepMilliseconds(200); // will raise 'code is unreachable' warning
}
if (i == 10) goto failed;
- // Read capacity
- uinfo("READ CAPACITY(10)...");
- res = scsi_readcapacity10(lunp, &u.cap);
- if (res.tres != MSD_TRANSACTIONRESULT_OK) {
- uerr("\tREAD CAPACITY(10): Transaction error");
- goto failed;
- } else if (res.cres == MSD_COMMANDRESULT_FAILED) {
- uerr("\tREAD CAPACITY(10): Command Failed");
- _requestsense(lunp);
- goto failed;
- } else if (res.cres == MSD_COMMANDRESULT_PHASE_ERROR) {
- //TODO: Do reset, etc.
- uerr("\tREAD CAPACITY(10): Command Phase Error");
- goto failed;
- }
- lunp->info.blk_size = __REV(u.cap.block_size);
- lunp->info.blk_num = __REV(u.cap.last_block_addr) + 1;
+ {
+ USBH_DEFINE_BUFFER(scsi_readcapacity10_response_t cap);
+ // Read capacity
+ uinfo("READ CAPACITY(10)...");
+ res = scsi_readcapacity10(lunp, &cap);
+ if (res == MSD_RESULT_DISCONNECTED) {
+ goto failed;
+ } else if (res == MSD_RESULT_TRANSPORT_ERROR) {
+ //retry?
+ goto failed;
+ } else if (res == MSD_RESULT_FAILED) {
+ //retry?
+ goto failed;
+ }
+
+ lunp->info.blk_size = __REV(cap.block_size);
+ lunp->info.blk_num = __REV(cap.last_block_addr) + 1;
+ }
+
uinfof("\tBlock size=%dbytes, blocks=%u (~%u MB)", lunp->info.blk_size, lunp->info.blk_num,
(uint32_t)(((uint64_t)lunp->info.blk_size * lunp->info.blk_num) / (1024UL * 1024UL)));
uinfo("MSD Connected.");
-
- osalMutexUnlock(&msdp->mtx);
- osalSysLock();
lunp->state = BLK_READY;
- osalSysUnlock();
-
+ chSemSignal(&lunp->sem);
return HAL_SUCCESS;
/* Connection failed, state reset to BLK_ACTIVE.*/
failed:
- osalMutexUnlock(&msdp->mtx);
- osalSysLock();
+ uinfo("MSD Connect failed.");
lunp->state = BLK_ACTIVE;
- osalSysUnlock();
+ chSemSignal(&lunp->sem);
return HAL_FAILED;
}
-
bool usbhmsdLUNDisconnect(USBHMassStorageLUNDriver *lunp) {
osalDbgCheck(lunp != NULL);
- osalSysLock();
- osalDbgAssert((lunp->state == BLK_ACTIVE) || (lunp->state == BLK_READY),
- "invalid state");
+
+ chSemWait(&lunp->sem);
+ osalDbgAssert((lunp->state == BLK_READY) || (lunp->state == BLK_ACTIVE), "invalid state");
if (lunp->state == BLK_ACTIVE) {
- osalSysUnlock();
+ chSemSignal(&lunp->sem);
return HAL_SUCCESS;
}
lunp->state = BLK_DISCONNECTING;
- osalSysUnlock();
- //TODO: complete
+ //TODO: complete: sync, etc.
- osalSysLock();
lunp->state = BLK_ACTIVE;
- osalSysUnlock();
+ chSemSignal(&lunp->sem);
+
return HAL_SUCCESS;
}
@@ -794,34 +831,29 @@ bool usbhmsdLUNRead(USBHMassStorageLUNDriver *lunp, uint32_t startblk,
bool ret = HAL_FAILED;
uint16_t blocks;
msd_result_t res;
+ uint32_t actual_len;
- osalSysLock();
+ chSemWait(&lunp->sem);
if (lunp->state != BLK_READY) {
- osalSysUnlock();
+ chSemSignal(&lunp->sem);
return ret;
}
lunp->state = BLK_READING;
- osalSysUnlock();
- osalMutexLock(&lunp->msdp->mtx);
while (n) {
if (n > 0xffff) {
blocks = 0xffff;
} else {
blocks = (uint16_t)n;
}
- res = scsi_read10(lunp, startblk, blocks, buffer);
- if (res.tres != MSD_TRANSACTIONRESULT_OK) {
- uerr("\tREAD (10): Transaction error");
+ res = scsi_read10(lunp, startblk, blocks, buffer, &actual_len);
+ if (res == MSD_RESULT_DISCONNECTED) {
goto exit;
- } else if (res.cres == MSD_COMMANDRESULT_FAILED) {
- //TODO: request sense, and act appropriately
- uerr("\tREAD (10): Command Failed");
- _requestsense(lunp);
+ } else if (res == MSD_RESULT_TRANSPORT_ERROR) {
+ //retry?
goto exit;
- } else if (res.cres == MSD_COMMANDRESULT_PHASE_ERROR) {
- //TODO: Do reset, etc.
- uerr("\tREAD (10): Command Phase Error");
+ } else if (res == MSD_RESULT_FAILED) {
+ //retry?
goto exit;
}
n -= blocks;
@@ -832,15 +864,8 @@ bool usbhmsdLUNRead(USBHMassStorageLUNDriver *lunp, uint32_t startblk,
ret = HAL_SUCCESS;
exit:
- osalMutexUnlock(&lunp->msdp->mtx);
- osalSysLock();
- if (lunp->state == BLK_READING) {
- lunp->state = BLK_READY;
- } else {
- osalDbgCheck(lunp->state == BLK_STOP);
- uwarn("MSD: State = BLK_STOP");
- }
- osalSysUnlock();
+ lunp->state = BLK_READY;
+ chSemSignal(&lunp->sem);
return ret;
}
@@ -851,34 +876,29 @@ bool usbhmsdLUNWrite(USBHMassStorageLUNDriver *lunp, uint32_t startblk,
bool ret = HAL_FAILED;
uint16_t blocks;
msd_result_t res;
+ uint32_t actual_len;
- osalSysLock();
+ chSemWait(&lunp->sem);
if (lunp->state != BLK_READY) {
- osalSysUnlock();
+ chSemSignal(&lunp->sem);
return ret;
}
lunp->state = BLK_WRITING;
- osalSysUnlock();
- osalMutexLock(&lunp->msdp->mtx);
while (n) {
if (n > 0xffff) {
blocks = 0xffff;
} else {
blocks = (uint16_t)n;
}
- res = scsi_write10(lunp, startblk, blocks, buffer);
- if (res.tres != MSD_TRANSACTIONRESULT_OK) {
- uerr("\tWRITE (10): Transaction error");
+ res = scsi_write10(lunp, startblk, blocks, buffer, &actual_len);
+ if (res == MSD_RESULT_DISCONNECTED) {
goto exit;
- } else if (res.cres == MSD_COMMANDRESULT_FAILED) {
- //TODO: request sense, and act appropriately
- uerr("\tWRITE (10): Command Failed");
- _requestsense(lunp);
+ } else if (res == MSD_RESULT_TRANSPORT_ERROR) {
+ //retry?
goto exit;
- } else if (res.cres == MSD_COMMANDRESULT_PHASE_ERROR) {
- //TODO: Do reset, etc.
- uerr("\tWRITE (10): Command Phase Error");
+ } else if (res == MSD_RESULT_FAILED) {
+ //retry?
goto exit;
}
n -= blocks;
@@ -889,15 +909,8 @@ bool usbhmsdLUNWrite(USBHMassStorageLUNDriver *lunp, uint32_t startblk,
ret = HAL_SUCCESS;
exit:
- osalMutexUnlock(&lunp->msdp->mtx);
- osalSysLock();
- if (lunp->state == BLK_WRITING) {
- lunp->state = BLK_READY;
- } else {
- osalDbgCheck(lunp->state == BLK_STOP);
- uwarn("MSD: State = BLK_STOP");
- }
- osalSysUnlock();
+ lunp->state = BLK_READY;
+ chSemSignal(&lunp->sem);
return ret;
}
@@ -911,29 +924,41 @@ bool usbhmsdLUNSync(USBHMassStorageLUNDriver *lunp) {
bool usbhmsdLUNGetInfo(USBHMassStorageLUNDriver *lunp, BlockDeviceInfo *bdip) {
osalDbgCheck(lunp != NULL);
osalDbgCheck(bdip != NULL);
- *bdip = lunp->info;
- return HAL_SUCCESS;
+
+ osalSysLock();
+ if (lunp->state >= BLK_READY) {
+ *bdip = lunp->info;
+ osalSysUnlock();
+ return HAL_SUCCESS;
+ }
+ osalSysUnlock();
+ return HAL_FAILED;
}
bool usbhmsdLUNIsInserted(USBHMassStorageLUNDriver *lunp) {
osalDbgCheck(lunp != NULL);
- blkstate_t state;
- osalSysLock();
- state = lunp->state;
- osalSysUnlock();
- return (state >= BLK_ACTIVE);
+ return (lunp->state >= BLK_ACTIVE);
}
bool usbhmsdLUNIsProtected(USBHMassStorageLUNDriver *lunp) {
osalDbgCheck(lunp != NULL);
+ //TODO: Implement
return FALSE;
}
-void usbhmsdObjectInit(USBHMassStorageDriver *msdp) {
+static void _msd_object_init(USBHMassStorageDriver *msdp) {
osalDbgCheck(msdp != NULL);
memset(msdp, 0, sizeof(*msdp));
msdp->info = &usbhmsdClassDriverInfo;
- osalMutexObjectInit(&msdp->mtx);
}
+static void _msd_init(void) {
+ uint8_t i;
+ for (i = 0; i < HAL_USBHMSD_MAX_INSTANCES; i++) {
+ _msd_object_init(&USBHMSD[i]);
+ }
+ for (i = 0; i < HAL_USBHMSD_MAX_LUNS; i++) {
+ _lun_object_init(&MSBLKD[i]);
+ }
+}
#endif
diff --git a/os/hal/src/usbh/hal_usbh_uvc.c b/os/hal/src/usbh/hal_usbh_uvc.c
index 09a0f1d..a795cd8 100644
--- a/os/hal/src/usbh/hal_usbh_uvc.c
+++ b/os/hal/src/usbh/hal_usbh_uvc.c
@@ -1,6 +1,6 @@
/*
- ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio
- Copyright (C) 2015 Diego Ismirlian, TISA, (dismirlian (at) google's mail)
+ ChibiOS - Copyright (C) 2006..2017 Giovanni Di Sirio
+ Copyright (C) 2015..2017 Diego Ismirlian, (dismirlian (at) google's mail)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,10 +13,9 @@
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.
-*/
+ */
#include "hal.h"
-#include "hal_usbh.h"
#if HAL_USBH_USE_UVC
@@ -28,6 +27,10 @@
#error "USBHUVC needs HAL_USBH_USE_IAD"
#endif
+#include <string.h>
+#include "usbh/dev/uvc.h"
+#include "usbh/internal.h"
+
#if USBHUVC_DEBUG_ENABLE_TRACE
#define udbgf(f, ...) usbDbgPrintf(f, ##__VA_ARGS__)
#define udbg(f, ...) usbDbgPuts(f, ##__VA_ARGS__)
@@ -61,28 +64,673 @@
#endif
-static usbh_baseclassdriver_t *uvc_load(usbh_device_t *dev,
+USBHUVCDriver USBHUVCD[HAL_USBHUVC_MAX_INSTANCES];
+
+static void _uvc_init(void);
+static usbh_baseclassdriver_t *_uvc_load(usbh_device_t *dev,
const uint8_t *descriptor, uint16_t rem);
-static void uvc_unload(usbh_baseclassdriver_t *drv);
+static void _uvc_unload(usbh_baseclassdriver_t *drv);
static const usbh_classdriver_vmt_t class_driver_vmt = {
- uvc_load,
- uvc_unload
+ _uvc_init,
+ _uvc_load,
+ _uvc_unload
};
const usbh_classdriverinfo_t usbhuvcClassDriverInfo = {
- 0x0e, 0x03, 0x00, "UVC", &class_driver_vmt
+ "UVC", &class_driver_vmt
};
+static bool _request(USBHUVCDriver *uvcdp,
+ uint8_t bRequest, uint8_t entity, uint8_t control,
+ uint16_t wLength, uint8_t *data, uint8_t interf) {
+
+ usbh_urbstatus_t res;
+
+ if (bRequest & 0x80) {
+ res = usbhControlRequest(uvcdp->dev,
+ USBH_REQTYPE_CLASSIN(USBH_REQTYPE_RECIP_INTERFACE),
+ bRequest,
+ ((control) << 8),
+ (interf) | ((entity) << 8),
+ wLength, data);
+ } else {
+ res = usbhControlRequest(uvcdp->dev,
+ USBH_REQTYPE_CLASSOUT(USBH_REQTYPE_RECIP_INTERFACE),
+ bRequest,
+ ((control) << 8),
+ (interf) | ((entity) << 8),
+ wLength, data);
+ }
+
+ if (res != USBH_URBSTATUS_OK)
+ return HAL_FAILED;
+
+ return HAL_SUCCESS;
+}
+
+bool usbhuvcVCRequest(USBHUVCDriver *uvcdp,
+ uint8_t bRequest, uint8_t entity, uint8_t control,
+ uint16_t wLength, uint8_t *data) {
+ return _request(uvcdp, bRequest, entity, control, wLength, data, if_get(&uvcdp->ivc)->bInterfaceNumber);
+}
+
+bool usbhuvcVSRequest(USBHUVCDriver *uvcdp,
+ uint8_t bRequest, uint8_t control,
+ uint16_t wLength, uint8_t *data) {
+
+ return _request(uvcdp, bRequest, 0, control, wLength, data, if_get(&uvcdp->ivs)->bInterfaceNumber);
+}
+
+static bool _set_vs_alternate(USBHUVCDriver *uvcdp, uint16_t min_ep_size) {
+
+ if (min_ep_size == 0) {
+ uinfo("Selecting Alternate setting 0");
+ return usbhStdReqSetInterface(uvcdp->dev, if_get(&uvcdp->ivs)->bInterfaceNumber, 0);
+ }
+
+ if_iterator_t iif = uvcdp->ivs;
+ generic_iterator_t iep;
+ const usbh_endpoint_descriptor_t *ep = NULL;
+ uint8_t alt = 0;
+ uint16_t sz = 0xffff;
+
+ uinfof("Searching alternate setting with min_ep_size=%d", min_ep_size);
+
+ for (; iif.valid; if_iter_next(&iif)) {
+ const usbh_interface_descriptor_t *const ifdesc = if_get(&iif);
+
+ if ((ifdesc->bInterfaceClass != UVC_CC_VIDEO)
+ || (ifdesc->bInterfaceSubClass != UVC_SC_VIDEOSTREAMING))
+ continue;
+
+ uinfof("\tScanning alternate setting=%d", ifdesc->bAlternateSetting);
+
+ if (ifdesc->bNumEndpoints == 0)
+ continue;
+
+ for (ep_iter_init(&iep, &iif); iep.valid; ep_iter_next(&iep)) {
+ const usbh_endpoint_descriptor_t *const epdesc = ep_get(&iep);
+ if (((epdesc->bmAttributes & 0x03) == USBH_EPTYPE_ISO)
+ && ((epdesc->bEndpointAddress & 0x80) == USBH_EPDIR_IN)) {
+
+ uinfof("\t Endpoint wMaxPacketSize = %d", epdesc->wMaxPacketSize);
+
+ if (epdesc->wMaxPacketSize >= min_ep_size) {
+ if (epdesc->wMaxPacketSize < sz) {
+ uinfo("\t Found new optimal alternate setting");
+ sz = epdesc->wMaxPacketSize;
+ alt = ifdesc->bAlternateSetting;
+ ep = epdesc;
+ }
+ }
+ }
+ }
+ }
+
+ if (ep && alt) {
+ uinfof("\tSelecting Alternate setting %d", alt);
+ if (usbhStdReqSetInterface(uvcdp->dev, if_get(&uvcdp->ivs)->bInterfaceNumber, alt) == HAL_SUCCESS) {
+ usbhEPObjectInit(&uvcdp->ep_iso, uvcdp->dev, ep);
+ usbhEPSetName(&uvcdp->ep_iso, "UVC[ISO ]");
+ return HAL_SUCCESS;
+ }
+ }
+
+ return HAL_FAILED;
+}
+
+#if USBH_DEBUG_ENABLE && USBHUVC_DEBUG_ENABLE_INFO
+void usbhuvcPrintProbeCommit(const usbh_uvc_ctrl_vs_probecommit_data_t *pc) {
+
+ //uinfof("UVC: probe/commit data:");
+ uinfof("\tbmHint=%04x", pc->bmHint);
+ uinfof("\tbFormatIndex=%d, bFrameIndex=%d, dwFrameInterval=%u",
+ pc->bFormatIndex, pc->bFrameIndex, pc->dwFrameInterval);
+ uinfof("\twKeyFrameRate=%d, wPFrameRate=%d, wCompQuality=%u, wCompWindowSize=%u",
+ pc->wKeyFrameRate, pc->wPFrameRate, pc->wCompQuality, pc->wCompWindowSize);
+ uinfof("\twDelay=%d", pc->wDelay);
+ uinfof("\tdwMaxVideoFrameSize=%u", pc->dwMaxVideoFrameSize);
+ uinfof("\tdwMaxPayloadTransferSize=%u", pc->dwMaxPayloadTransferSize);
+/* uinfof("\tdwClockFrequency=%u", pc->dwClockFrequency);
+ uinfof("\tbmFramingInfo=%02x", pc->bmFramingInfo);
+ uinfof("\tbPreferedVersion=%d, bMinVersion=%d, bMaxVersion=%d",
+ pc->bPreferedVersion, pc->bMinVersion, pc->bMaxVersion); */
+}
+#endif
+
+static void _post(USBHUVCDriver *uvcdp, usbh_urb_t *urb, memory_pool_t *mp, uint16_t type) {
+ usbhuvc_message_base_t *const msg = (usbhuvc_message_base_t *)((uint8_t *)urb->buff - offsetof(usbhuvc_message_data_t, data));
+ msg->timestamp = osalOsGetSystemTimeX();
+
+ usbhuvc_message_base_t *const new_msg = (usbhuvc_message_base_t *)chPoolAllocI(mp);
+ if (new_msg != NULL) {
+ /* allocated the new buffer, now try to post the message to the mailbox */
+ if (chMBPostI(&uvcdp->mb, (msg_t)msg) == MSG_OK) {
+ /* everything OK, complete the missing fields */
+ msg->type = type;
+ msg->length = urb->actualLength;
+
+ /* change the URB's buffer to the newly allocated one */
+ urb->buff = ((usbhuvc_message_data_t *)new_msg)->data;
+ } else {
+ /* couldn't post the message, free the newly allocated buffer */
+ uerr("UVC: error, mailbox overrun");
+ chPoolFreeI(&uvcdp->mp_status, new_msg);
+ }
+ } else {
+ uerrf("UVC: error, %s pool overrun", mp == &uvcdp->mp_data ? "data" : "status");
+ }
+}
+
+static void _cb_int(usbh_urb_t *urb) {
+ USBHUVCDriver *uvcdp = (USBHUVCDriver *)urb->userData;
+
+ switch (urb->status) {
+ case USBH_URBSTATUS_OK:
+ if (urb->actualLength >= 2) {
+ _post(uvcdp, urb, &uvcdp->mp_status, USBHUVC_MESSAGETYPE_STATUS);
+ } else {
+ uerrf("UVC: INT IN, actualLength=%d", urb->actualLength);
+ }
+ break;
+ case USBH_URBSTATUS_TIMEOUT: /* the device NAKed */
+ udbg("UVC: INT IN no info");
+ break;
+ case USBH_URBSTATUS_DISCONNECTED:
+ case USBH_URBSTATUS_CANCELLED:
+ uwarn("UVC: INT IN status = DISCONNECTED/CANCELLED, aborting");
+ return;
+ default:
+ uerrf("UVC: INT IN error, unexpected status = %d", urb->status);
+ break;
+ }
+
+ usbhURBObjectResetI(urb);
+ usbhURBSubmitI(urb);
+}
+
+static void _cb_iso(usbh_urb_t *urb) {
+ USBHUVCDriver *uvcdp = (USBHUVCDriver *)urb->userData;
+
+ if ((urb->status == USBH_URBSTATUS_DISCONNECTED)
+ || (urb->status == USBH_URBSTATUS_CANCELLED)) {
+ uwarn("UVC: ISO IN status = DISCONNECTED/CANCELLED, aborting");
+ return;
+ }
+
+ if (urb->status != USBH_URBSTATUS_OK) {
+ uerrf("UVC: ISO IN error, unexpected status = %d", urb->status);
+ } else if (urb->actualLength >= 2) {
+ const uint8_t *const buff = (const uint8_t *)urb->buff;
+ if (buff[0] < 2) {
+ uerrf("UVC: ISO IN, bHeaderLength=%d", buff[0]);
+ } else if (buff[0] > urb->actualLength) {
+ uerrf("UVC: ISO IN, bHeaderLength=%d > actualLength=%d", buff[0], urb->actualLength);
+ } else {
+ udbgf("UVC: ISO IN len=%d, hdr=%d, FID=%d, EOF=%d, ERR=%d, EOH=%d",
+ urb->actualLength,
+ buff[0],
+ buff[1] & UVC_HDR_FID,
+ buff[1] & UVC_HDR_EOF,
+ buff[1] & UVC_HDR_ERR,
+ buff[1] & UVC_HDR_EOH);
+
+ if ((urb->actualLength > buff[0])
+ || (buff[1] & (UVC_HDR_EOF | UVC_HDR_ERR))) {
+ _post(uvcdp, urb, &uvcdp->mp_data, USBHUVC_MESSAGETYPE_DATA);
+ } else {
+ udbgf("UVC: ISO IN skip: len=%d, hdr=%d, FID=%d, EOF=%d, ERR=%d, EOH=%d",
+ urb->actualLength,
+ buff[0],
+ buff[1] & UVC_HDR_FID,
+ buff[1] & UVC_HDR_EOF,
+ buff[1] & UVC_HDR_ERR,
+ buff[1] & UVC_HDR_EOH);
+ }
+ }
+ } else if (urb->actualLength > 0) {
+ uerrf("UVC: ISO IN, actualLength=%d", urb->actualLength);
+ }
+
+ usbhURBObjectResetI(urb);
+ usbhURBSubmitI(urb);
+}
+
+
+bool usbhuvcStreamStart(USBHUVCDriver *uvcdp, uint16_t min_ep_sz) {
+ bool ret = HAL_FAILED;
+
+ osalSysLock();
+ osalDbgCheck(uvcdp && (uvcdp->state != USBHUVC_STATE_UNINITIALIZED) &&
+ (uvcdp->state != USBHUVC_STATE_BUSY));
+ if (uvcdp->state == USBHUVC_STATE_STREAMING) {
+ osalSysUnlock();
+ return HAL_SUCCESS;
+ }
+ if (uvcdp->state != USBHUVC_STATE_READY) {
+ osalSysUnlock();
+ return HAL_FAILED;
+ }
+ uvcdp->state = USBHUVC_STATE_BUSY;
+ osalSysUnlock();
+
+ uint32_t workramsz;
+ const uint8_t *elem;
+ uint32_t datapackets;
+ uint32_t data_sz;
+
+ //set the alternate setting
+ if (_set_vs_alternate(uvcdp, min_ep_sz) != HAL_SUCCESS)
+ goto exit;
+
+ //reserve working RAM
+ data_sz = (uvcdp->ep_iso.wMaxPacketSize + sizeof(usbhuvc_message_data_t) + 3) & ~3;
+ datapackets = HAL_USBHUVC_WORK_RAM_SIZE / data_sz;
+ if (datapackets == 0) {
+ uerr("Not enough work RAM");
+ goto failed;
+ }
+
+ workramsz = datapackets * data_sz;
+ uinfof("Reserving %u bytes of RAM (%d data packets of %d bytes)", workramsz, datapackets, data_sz);
+ if (datapackets > (HAL_USBHUVC_MAX_MAILBOX_SZ - HAL_USBHUVC_STATUS_PACKETS_COUNT)) {
+ uwarn("Mailbox may overflow, use a larger HAL_USBHUVC_MAX_MAILBOX_SZ. UVC will under-utilize the assigned work RAM.");
+ }
+ chMBResumeX(&uvcdp->mb);
+
+ uvcdp->mp_data_buffer = chHeapAlloc(NULL, workramsz);
+ if (uvcdp->mp_data_buffer == NULL) {
+ uerr("Couldn't reserve RAM");
+ goto failed;
+ }
+
+ //initialize the mempool
+ chPoolObjectInit(&uvcdp->mp_data, data_sz, NULL);
+ elem = (const uint8_t *)uvcdp->mp_data_buffer;
+ while (datapackets--) {
+ chPoolFree(&uvcdp->mp_data, (void *)elem);
+ elem += data_sz;
+ }
+
+ //open the endpoint
+ usbhEPOpen(&uvcdp->ep_iso);
+
+ //allocate 1 buffer and submit the first transfer
+ {
+ usbhuvc_message_data_t *const msg = (usbhuvc_message_data_t *)chPoolAlloc(&uvcdp->mp_data);
+ osalDbgCheck(msg);
+ usbhURBObjectInit(&uvcdp->urb_iso, &uvcdp->ep_iso, _cb_iso, uvcdp, msg->data, uvcdp->ep_iso.wMaxPacketSize);
+ }
+
+ usbhURBSubmit(&uvcdp->urb_iso);
+
+ ret = HAL_SUCCESS;
+ goto exit;
+
+failed:
+ _set_vs_alternate(uvcdp, 0);
+ if (uvcdp->mp_data_buffer)
+ chHeapFree(uvcdp->mp_data_buffer);
+
+exit:
+ osalSysLock();
+ if (ret == HAL_SUCCESS)
+ uvcdp->state = USBHUVC_STATE_STREAMING;
+ else
+ uvcdp->state = USBHUVC_STATE_READY;
+ osalSysUnlock();
+ return ret;
+}
+
+bool usbhuvcStreamStop(USBHUVCDriver *uvcdp) {
+ osalSysLock();
+ osalDbgCheck(uvcdp && (uvcdp->state != USBHUVC_STATE_UNINITIALIZED) &&
+ (uvcdp->state != USBHUVC_STATE_BUSY));
+ if (uvcdp->state != USBHUVC_STATE_STREAMING) {
+ osalSysUnlock();
+ return HAL_SUCCESS;
+ }
+ uvcdp->state = USBHUVC_STATE_BUSY;
-static usbh_baseclassdriver_t *uvc_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem) {
- (void)dev;
- (void)descriptor;
- (void)rem;
+ //close the ISO endpoint
+ usbhEPCloseS(&uvcdp->ep_iso);
+
+ //purge the mailbox
+ chMBResetI(&uvcdp->mb); //TODO: the status messages are lost!!
+ chMtxLockS(&uvcdp->mtx);
+ osalSysUnlock();
+
+ //free the working memory
+ chHeapFree(uvcdp->mp_data_buffer);
+ uvcdp->mp_data_buffer = 0;
+
+ //set alternate setting to 0
+ _set_vs_alternate(uvcdp, 0);
+
+ osalSysLock();
+ uvcdp->state = USBHUVC_STATE_READY;
+ chMtxUnlockS(&uvcdp->mtx);
+ osalSysUnlock();
+ return HAL_SUCCESS;
+}
+
+bool usbhuvcFindVSDescriptor(USBHUVCDriver *uvcdp,
+ generic_iterator_t *ics,
+ uint8_t bDescriptorSubtype,
+ bool start) {
+
+ if (start)
+ cs_iter_init(ics, (generic_iterator_t *)&uvcdp->ivs);
+ else
+ cs_iter_next(ics);
+
+ for (; ics->valid; cs_iter_next(ics)) {
+ if (ics->curr[1] != UVC_CS_INTERFACE)
+ break;
+ if (ics->curr[2] == bDescriptorSubtype)
+ return HAL_SUCCESS;
+ if (!start)
+ break;
+ }
+ return HAL_FAILED;
+}
+
+void usbhuvcResetPC(USBHUVCDriver *uvcdp) {
+ memset(&uvcdp->pc, 0, sizeof(uvcdp->pc));
+}
+
+bool usbhuvcProbe(USBHUVCDriver *uvcdp) {
+// memset(&uvcdp->pc_min, 0, sizeof(uvcdp->pc_min));
+// memset(&uvcdp->pc_max, 0, sizeof(uvcdp->pc_max));
+
+ if (usbhuvcVSRequest(uvcdp, UVC_SET_CUR, UVC_CTRL_VS_PROBE_CONTROL, sizeof(uvcdp->pc), (uint8_t *)&uvcdp->pc) != HAL_SUCCESS)
+ return HAL_FAILED;
+ if (usbhuvcVSRequest(uvcdp, UVC_GET_CUR, UVC_CTRL_VS_PROBE_CONTROL, sizeof(uvcdp->pc), (uint8_t *)&uvcdp->pc) != HAL_SUCCESS)
+ return HAL_FAILED;
+ if (usbhuvcVSRequest(uvcdp, UVC_GET_MAX, UVC_CTRL_VS_PROBE_CONTROL, sizeof(uvcdp->pc_max), (uint8_t *)&uvcdp->pc_max) != HAL_SUCCESS)
+ return HAL_FAILED;
+ if (usbhuvcVSRequest(uvcdp, UVC_GET_MIN, UVC_CTRL_VS_PROBE_CONTROL, sizeof(uvcdp->pc_min), (uint8_t *)&uvcdp->pc_min) != HAL_SUCCESS)
+ return HAL_FAILED;
+ return HAL_SUCCESS;
+}
+
+bool usbhuvcCommit(USBHUVCDriver *uvcdp) {
+ if (usbhuvcVSRequest(uvcdp, UVC_SET_CUR, UVC_CTRL_VS_COMMIT_CONTROL, sizeof(uvcdp->pc), (uint8_t *)&uvcdp->pc) != HAL_SUCCESS)
+ return HAL_FAILED;
+
+ osalSysLock();
+ if (uvcdp->state == USBHUVC_STATE_ACTIVE)
+ uvcdp->state = USBHUVC_STATE_READY;
+ osalSysUnlock();
+ return HAL_SUCCESS;
+}
+
+uint32_t usbhuvcEstimateRequiredEPSize(USBHUVCDriver *uvcdp, const uint8_t *formatdesc,
+ const uint8_t *framedesc, uint32_t dwFrameInterval) {
+
+ osalDbgCheck(framedesc);
+ osalDbgCheck(framedesc[0] > 3);
+ osalDbgCheck(framedesc[1] == UVC_CS_INTERFACE);
+ osalDbgCheck(formatdesc);
+ osalDbgCheck(formatdesc[0] > 3);
+ osalDbgCheck(formatdesc[1] == UVC_CS_INTERFACE);
+
+ uint16_t w, h, div, mul;
+ uint8_t bpp;
+
+ switch (framedesc[2]) {
+ case UVC_VS_FRAME_MJPEG: {
+ const usbh_uvc_frame_mjpeg_t *frame = (const usbh_uvc_frame_mjpeg_t *)framedesc;
+ //const usbh_uvc_format_mjpeg_t *fmt = (const usbh_uvc_format_mjpeg_t *)formatdesc;
+ w = frame->wWidth;
+ h = frame->wHeight;
+ bpp = 16; //TODO: check this!!
+ mul = 1;
+ div = 5; //TODO: check this estimate
+ } break;
+ case UVC_VS_FRAME_UNCOMPRESSED: {
+ const usbh_uvc_frame_uncompressed_t *frame = (const usbh_uvc_frame_uncompressed_t *)framedesc;
+ const usbh_uvc_format_uncompressed *fmt = (const usbh_uvc_format_uncompressed *)formatdesc;
+ w = frame->wWidth;
+ h = frame->wHeight;
+ bpp = fmt->bBitsPerPixel;
+ mul = div = 1;
+ } break;
+ default:
+ uwarn("Unsupported format");
+ return 0xffffffff;
+ }
+
+ uint32_t sz = w * h / 8 * bpp;
+ sz *= 10000000UL / dwFrameInterval;
+ sz /= 1000;
+
+ if (uvcdp->dev->speed == USBH_DEVSPEED_HIGH)
+ div *= 8;
+
+ return (sz * mul) / div + 12;
+}
+
+static usbh_baseclassdriver_t *_uvc_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem) {
+
+ USBHUVCDriver *uvcdp;
+ uint8_t i;
+
+ if (_usbh_match_descriptor(descriptor, rem, USBH_DT_INTERFACE_ASSOCIATION,
+ 0x0e, 0x03, 0x00) != HAL_SUCCESS)
+ return NULL;
+
+ /* alloc driver */
+ for (i = 0; i < HAL_USBHUVC_MAX_INSTANCES; i++) {
+ if (USBHUVCD[i].dev == NULL) {
+ uvcdp = &USBHUVCD[i];
+ goto alloc_ok;
+ }
+ }
+
+ uwarn("Can't alloc UVC driver");
+
+ /* can't alloc */
return NULL;
+
+alloc_ok:
+ /* initialize the driver's variables */
+ uvcdp->ivc.curr = uvcdp->ivs.curr = NULL;
+
+ usbhEPSetName(&dev->ctrl, "UVC[CTRL]");
+
+ const usbh_ia_descriptor_t *iad = (const usbh_ia_descriptor_t *)descriptor;
+ if_iterator_t iif;
+ generic_iterator_t ics;
+ generic_iterator_t iep;
+
+ iif.iad = iad;
+ iif.curr = descriptor;
+ iif.rem = rem;
+
+ for (if_iter_next(&iif); iif.valid; if_iter_next(&iif)) {
+ if (iif.iad != iad) break;
+
+ const usbh_interface_descriptor_t *const ifdesc = if_get(&iif);
+ if (ifdesc->bInterfaceClass != UVC_CC_VIDEO) {
+ uwarnf("Skipping Interface %d (class != UVC_CC_VIDEO)",
+ ifdesc->bInterfaceNumber);
+ continue;
+ }
+
+ uinfof("Interface %d, Alt=%d, Class=UVC_CC_VIDEO, Subclass=%02x",
+ ifdesc->bInterfaceNumber,
+ ifdesc->bAlternateSetting,
+ ifdesc->bInterfaceSubClass);
+
+ switch (ifdesc->bInterfaceSubClass) {
+ case UVC_SC_VIDEOCONTROL:
+ if (uvcdp->ivc.curr == NULL) {
+ uvcdp->ivc = iif;
+ }
+ for (cs_iter_init(&ics, (generic_iterator_t *)&iif); ics.valid; cs_iter_next(&ics)) {
+ if (ics.curr[1] != UVC_CS_INTERFACE) {
+ uwarnf("Unknown descriptor=%02X", ics.curr[1]);
+ continue;
+ }
+ switch (ics.curr[2]) {
+ case UVC_VC_HEADER:
+ uinfo(" VC_HEADER"); break;
+ case UVC_VC_INPUT_TERMINAL:
+ uinfof(" VC_INPUT_TERMINAL, ID=%d", ics.curr[3]); break;
+ case UVC_VC_OUTPUT_TERMINAL:
+ uinfof(" VC_OUTPUT_TERMINAL, ID=%d", ics.curr[3]); break;
+ case UVC_VC_SELECTOR_UNIT:
+ uinfof(" VC_SELECTOR_UNIT, ID=%d", ics.curr[3]); break;
+ case UVC_VC_PROCESSING_UNIT:
+ uinfof(" VC_PROCESSING_UNIT, ID=%d", ics.curr[3]); break;
+ case UVC_VC_EXTENSION_UNIT:
+ uinfof(" VC_EXTENSION_UNIT, ID=%d", ics.curr[3]); break;
+ default:
+ uwarnf("Unknown video bDescriptorSubtype=%02x", ics.curr[2]);
+ break;
+ }
+ }
+ break;
+ case UVC_SC_VIDEOSTREAMING:
+ if (uvcdp->ivs.curr == NULL) {
+ uvcdp->ivs = iif;
+ }
+ for (cs_iter_init(&ics, (generic_iterator_t *)&iif); ics.valid; cs_iter_next(&ics)) {
+ if (ics.curr[1] != UVC_CS_INTERFACE) {
+ uwarnf("Unknown descriptor=%02X", ics.curr[1]);
+ continue;
+ }
+ switch (ics.curr[2]) {
+ case UVC_VS_INPUT_HEADER:
+ uinfo(" VS_INPUT_HEADER"); break;
+ case UVC_VS_OUTPUT_HEADER:
+ uinfo(" VS_OUTPUT_HEADER"); break;
+ case UVC_VS_STILL_IMAGE_FRAME:
+ uinfo(" VS_STILL_IMAGE_FRAME"); break;
+
+ case UVC_VS_FORMAT_UNCOMPRESSED:
+ uinfof(" VS_FORMAT_UNCOMPRESSED, bFormatIndex=%d", ics.curr[3]); break;
+ case UVC_VS_FORMAT_MPEG2TS:
+ uinfof(" VS_FORMAT_MPEG2TS, bFormatIndex=%d", ics.curr[3]); break;
+ case UVC_VS_FORMAT_DV:
+ uinfof(" VS_FORMAT_DV, bFormatIndex=%d", ics.curr[3]); break;
+ case UVC_VS_FORMAT_MJPEG:
+ uinfof(" VS_FORMAT_MJPEG, bFormatIndex=%d", ics.curr[3]); break;
+ case UVC_VS_FORMAT_FRAME_BASED:
+ uinfof(" VS_FORMAT_FRAME_BASED, bFormatIndex=%d", ics.curr[3]); break;
+ case UVC_VS_FORMAT_STREAM_BASED:
+ uinfof(" VS_FORMAT_STREAM_BASED, bFormatIndex=%d", ics.curr[3]); break;
+
+ case UVC_VS_FRAME_UNCOMPRESSED:
+ uinfof(" VS_FRAME_UNCOMPRESSED, bFrameIndex=%d", ics.curr[3]); break;
+ case UVC_VS_FRAME_MJPEG:
+ uinfof(" VS_FRAME_MJPEG, bFrameIndex=%d", ics.curr[3]); break;
+ case UVC_VS_FRAME_FRAME_BASED:
+ uinfof(" VS_FRAME_FRAME_BASED, bFrameIndex=%d", ics.curr[3]); break;
+
+ case UVC_VS_COLOR_FORMAT:
+ uinfo(" VS_COLOR_FORMAT"); break;
+ default:
+ uwarnf("Unknown video bDescriptorSubtype=%02x", ics.curr[2]);
+ break;
+ }
+ }
+ break;
+ default:
+ uwarnf("Unknown video bInterfaceSubClass=%02x", ifdesc->bInterfaceSubClass);
+ break;
+ }
+
+ for (ep_iter_init(&iep, &iif); iep.valid; ep_iter_next(&iep)) {
+ const usbh_endpoint_descriptor_t *const epdesc = ep_get(&iep);
+
+ if ((ifdesc->bInterfaceSubClass == UVC_SC_VIDEOCONTROL)
+ && ((epdesc->bmAttributes & 0x03) == USBH_EPTYPE_INT)
+ && ((epdesc->bEndpointAddress & 0x80) == USBH_EPDIR_IN)) {
+ /* found VC interrupt endpoint */
+ uinfof(" VC Interrupt endpoint; %02x, bInterval=%d",
+ epdesc->bEndpointAddress, epdesc->bInterval);
+ usbhEPObjectInit(&uvcdp->ep_int, dev, epdesc);
+ usbhEPSetName(&uvcdp->ep_int, "UVC[INT ]");
+ } else if ((ifdesc->bInterfaceSubClass == UVC_SC_VIDEOSTREAMING)
+ && ((epdesc->bmAttributes & 0x03) == USBH_EPTYPE_ISO)
+ && ((epdesc->bEndpointAddress & 0x80) == USBH_EPDIR_IN)) {
+ /* found VS isochronous endpoint */
+ uinfof(" VS Isochronous endpoint; %02x, bInterval=%d, bmAttributes=%02x",
+ epdesc->bEndpointAddress, epdesc->bInterval, epdesc->bmAttributes);
+ } else {
+ /* unknown EP */
+ uwarnf(" <unknown endpoint>, bEndpointAddress=%02x, bmAttributes=%02x",
+ epdesc->bEndpointAddress, epdesc->bmAttributes);
+ }
+
+ for (cs_iter_init(&ics, &iep); ics.valid; cs_iter_next(&ics)) {
+ uinfof(" CS_ENDPOINT bLength=%d, bDescriptorType=%02X",
+ ics.curr[0], ics.curr[1]);
+ }
+ }
+ }
+
+ if ((uvcdp->ivc.curr == NULL) || (uvcdp->ivs.curr == NULL)) {
+ return NULL;
+ }
+
+// uvcdp->dev = dev;
+
+ _set_vs_alternate(uvcdp, 0);
+
+ /* initialize the INT endpoint */
+ chPoolObjectInit(&uvcdp->mp_status, sizeof(usbhuvc_message_status_t), NULL);
+ for(i = 0; i < HAL_USBHUVC_STATUS_PACKETS_COUNT; i++)
+ chPoolFree(&uvcdp->mp_status, &uvcdp->mp_status_buffer[i]);
+
+ usbhEPOpen(&uvcdp->ep_int);
+
+ usbhuvc_message_status_t *const msg = (usbhuvc_message_status_t *)chPoolAlloc(&uvcdp->mp_status);
+ osalDbgCheck(msg);
+ usbhURBObjectInit(&uvcdp->urb_int, &uvcdp->ep_int, _cb_int, uvcdp, msg->data, USBHUVC_MAX_STATUS_PACKET_SZ);
+ osalSysLock();
+ usbhURBSubmitI(&uvcdp->urb_int);
+ uvcdp->state = USBHUVC_STATE_ACTIVE;
+ osalOsRescheduleS(); /* because of usbhURBSubmitI */
+ osalSysUnlock();
+
+ dev->keepFullCfgDesc++;
+ return (usbh_baseclassdriver_t *)uvcdp;
+}
+
+static void _uvc_unload(usbh_baseclassdriver_t *drv) {
+ USBHUVCDriver *const uvcdp = (USBHUVCDriver *)drv;
+
+ usbhuvcStreamStop(uvcdp);
+
+ usbhEPClose(&uvcdp->ep_int);
+
+ //TODO: free
+
+ if (drv->dev->keepFullCfgDesc)
+ drv->dev->keepFullCfgDesc--;
+
+ osalSysLock();
+ uvcdp->state = USBHUVC_STATE_STOP;
+ osalSysUnlock();
+}
+
+static void _object_init(USBHUVCDriver *uvcdp) {
+ osalDbgCheck(uvcdp != NULL);
+ memset(uvcdp, 0, sizeof(*uvcdp));
+ uvcdp->info = &usbhuvcClassDriverInfo;
+ chMBObjectInit(&uvcdp->mb, uvcdp->mb_buff, HAL_USBHUVC_MAX_MAILBOX_SZ);
+ chMtxObjectInit(&uvcdp->mtx);
+ uvcdp->state = USBHUVC_STATE_STOP;
}
-static void uvc_unload(usbh_baseclassdriver_t *drv) {
- (void)drv;
+static void _uvc_init(void) {
+ uint8_t i;
+ for (i = 0; i < HAL_USBHUVC_MAX_INSTANCES; i++) {
+ _object_init(&USBHUVCD[i]);
+ }
}
#endif