-
Notifications
You must be signed in to change notification settings - Fork 298
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(platform/nuttx): update fs & net & cdcacm support
Signed-off-by: sakumisu <[email protected]>
- Loading branch information
Showing
6 changed files
with
650 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,275 @@ | ||
/* | ||
* Copyright (c) 2025, sakumisu | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
#include <nuttx/kmalloc.h> | ||
#include <nuttx/mutex.h> | ||
#include <nuttx/semaphore.h> | ||
#include <fcntl.h> | ||
|
||
#include "usbd_core.h" | ||
#include "usbd_cdc_acm.h" | ||
|
||
#include <nuttx/mm/circbuf.h> | ||
|
||
#ifndef CONFIG_USBDEV_CDCACM_RXBUFSIZE | ||
#define CONFIG_USBDEV_CDCACM_RXBUFSIZE 512 | ||
#endif | ||
|
||
#ifndef CONFIG_USBDEV_CDCACM_TXBUFSIZE | ||
#define CONFIG_USBDEV_CDCACM_TXBUFSIZE 512 | ||
#endif | ||
|
||
USB_NOCACHE_RAM_SECTION struct usbdev_serial_s { | ||
char name[16]; | ||
struct circbuf_s circ; | ||
uint8_t inep; | ||
uint8_t outep; | ||
struct usbd_interface ctrl_intf; | ||
struct usbd_interface data_intf; | ||
__attribute__((aligned(32))) uint8_t cache_tempbuffer[512]; | ||
__attribute__((aligned(32))) uint8_t cache_rxbuffer[CONFIG_USBDEV_CDCACM_RXBUFSIZE]; | ||
__attribute__((aligned(32))) uint8_t cache_txbuffer[CONFIG_USBDEV_CDCACM_TXBUFSIZE]; | ||
}; | ||
|
||
struct usbdev_serial_ep_s { | ||
uint32_t rxlen; | ||
int error; | ||
sem_t txdone_sem; | ||
sem_t rxdone_sem; | ||
bool used; | ||
}; | ||
|
||
struct usbdev_serial_s *g_usb_cdcacm_serial[8] = { 0 }; | ||
struct usbdev_serial_ep_s g_usb_cdcacm_serial_ep[2][8] = { 0 }; | ||
|
||
void usbd_cdc_acm_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes) | ||
{ | ||
g_usb_cdcacm_serial_ep[0][ep & 0x0f].error = 0; | ||
g_usb_cdcacm_serial_ep[0][ep & 0x0f].rxlen = nbytes; | ||
nxsem_post(&g_usb_cdcacm_serial_ep[0][ep & 0x0f].rxdone_sem); | ||
} | ||
|
||
void usbd_cdc_acm_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes) | ||
{ | ||
if ((nbytes % usbd_get_ep_mps(busid, ep)) == 0 && nbytes) { | ||
/* send zlp */ | ||
usbd_ep_start_write(busid, ep, NULL, 0); | ||
} else { | ||
nxsem_post(&g_usb_cdcacm_serial_ep[1][ep & 0x0f].txdone_sem); | ||
} | ||
} | ||
|
||
/* Character driver methods */ | ||
|
||
static int usbdev_open(FAR struct file *filep); | ||
static int usbdev_close(FAR struct file *filep); | ||
static ssize_t usbdev_read(FAR struct file *filep, FAR char *buffer, | ||
size_t buflen); | ||
static ssize_t usbdev_write(FAR struct file *filep, | ||
FAR const char *buffer, size_t buflen); | ||
|
||
/**************************************************************************** | ||
* Private Data | ||
****************************************************************************/ | ||
|
||
static const struct file_operations g_usbdevops = { | ||
usbdev_open, /* open */ | ||
usbdev_close, /* close */ | ||
usbdev_read, /* read */ | ||
usbdev_write, /* write */ | ||
NULL, /* seek */ | ||
NULL, /* ioctl */ | ||
NULL, /* mmap */ | ||
NULL, /* truncate */ | ||
NULL /* poll */ | ||
}; | ||
|
||
static int usbdev_open(FAR struct file *filep) | ||
{ | ||
FAR struct inode *inode = filep->f_inode; | ||
|
||
DEBUGASSERT(inode->i_private); | ||
|
||
if (usb_device_is_configured(0)) { | ||
return OK; | ||
} else { | ||
return -ENODEV; | ||
} | ||
} | ||
|
||
static int usbdev_close(FAR struct file *filep) | ||
{ | ||
FAR struct inode *inode = filep->f_inode; | ||
|
||
DEBUGASSERT(inode->i_private); | ||
|
||
if (!usb_device_is_configured(0)) { | ||
return -ENODEV; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static ssize_t usbdev_read(FAR struct file *filep, FAR char *buffer, | ||
size_t buflen) | ||
{ | ||
FAR struct inode *inode = filep->f_inode; | ||
struct usbdev_serial_s *serial; | ||
int ret; | ||
|
||
DEBUGASSERT(inode->i_private); | ||
serial = (struct usbdev_serial_s *)inode->i_private; | ||
|
||
if (!usb_device_is_configured(0)) { | ||
return -ENODEV; | ||
} | ||
|
||
while (circbuf_used(&serial->circ) == 0) { | ||
nxsem_reset(&g_usb_cdcacm_serial_ep[0][serial->outep & 0x0f].rxdone_sem, 0); | ||
usbd_ep_start_read(0, serial->outep, serial->cache_tempbuffer, usbd_get_ep_mps(0, serial->outep)); | ||
ret = nxsem_wait(&g_usb_cdcacm_serial_ep[0][serial->outep & 0x0f].rxdone_sem); | ||
if (ret < 0) { | ||
return ret; | ||
} | ||
if (g_usb_cdcacm_serial_ep[0][serial->outep & 0x0f].error < 0) { | ||
return g_usb_cdcacm_serial_ep[0][serial->outep & 0x0f].error; | ||
} | ||
#if defined(CONFIG_ARCH_DCACHE) && !defined(CONFIG_USB_DCACHE_ENABLE) | ||
up_invalidate_dcache((uintptr_t)serial->cache_tempbuffer, (uintptr_t)(serial->cache_tempbuffer + USB_ALIGN_UP(g_usb_cdcacm_serial_ep[0][serial->outep & 0x0f].rxlen, 64))); | ||
#endif | ||
circbuf_overwrite(&serial->circ, serial->cache_tempbuffer, g_usb_cdcacm_serial_ep[0][serial->outep & 0x0f].rxlen); | ||
} | ||
return circbuf_read(&serial->circ, buffer, buflen); | ||
} | ||
|
||
static ssize_t usbdev_write(FAR struct file *filep, FAR const char *buffer, | ||
size_t buflen) | ||
{ | ||
FAR struct inode *inode = filep->f_inode; | ||
struct usbdev_serial_s *serial; | ||
int ret; | ||
|
||
DEBUGASSERT(inode->i_private); | ||
serial = (struct usbdev_serial_s *)inode->i_private; | ||
|
||
if (!usb_device_is_configured(0)) { | ||
return -ENODEV; | ||
} | ||
|
||
#ifdef CONFIG_ARCH_DCACHE | ||
uint32_t write_len = 0; | ||
|
||
while (write_len < buflen) { | ||
uint32_t len = buflen - write_len; | ||
if (len > CONFIG_USBDEV_CDCACM_TXBUFSIZE) { | ||
len = CONFIG_USBDEV_CDCACM_TXBUFSIZE; | ||
} | ||
memcpy(serial->cache_txbuffer, buffer + write_len, len); | ||
#ifndef CONFIG_USB_DCACHE_ENABLE | ||
up_clean_dcache((uintptr_t)serial->cache_txbuffer, (uintptr_t)(serial->cache_txbuffer + USB_ALIGN_UP(len, 64))); | ||
#endif | ||
nxsem_reset(&g_usb_cdcacm_serial_ep[0][serial->inep & 0x0f].txdone_sem, 0); | ||
usbd_ep_start_write(0, serial->inep, serial->cache_txbuffer, len); | ||
ret = nxsem_wait(&g_usb_cdcacm_serial_ep[0][serial->inep & 0x0f].txdone_sem); | ||
if (ret < 0) { | ||
return ret; | ||
} else { | ||
if (g_usb_cdcacm_serial_ep[1][serial->inep & 0x0f].error < 0) { | ||
return g_usb_cdcacm_serial_ep[1][serial->inep & 0x0f].error; | ||
} | ||
write_len += len; | ||
} | ||
} | ||
return buflen; | ||
#else | ||
nxsem_reset(&g_usb_cdcacm_serial_ep[0][outep & 0x0f].txdone_sem, 0); | ||
usbd_ep_start_write(0, serial->inep, buffer, buflen); | ||
ret = nxsem_wait(&g_usb_cdcacm_serial_ep[0][outep & 0x0f].txdone_sem); | ||
if (ret < 0) { | ||
return ret; | ||
} else { | ||
if (g_usb_cdcacm_serial_ep[1][serial->inep & 0x0f].error < 0) { | ||
return g_usb_cdcacm_serial_ep[1][serial->inep & 0x0f].error; | ||
} | ||
return buflen; | ||
} | ||
#endif | ||
} | ||
|
||
static struct usbd_endpoint cdc_out_ep[8] = { 0 }; | ||
static struct usbd_endpoint cdc_in_ep[8] = { 0 }; | ||
|
||
static void cdcacm_notify_handler(uint8_t busid, uint8_t event, void *arg) | ||
{ | ||
switch (event) { | ||
case USBD_EVENT_RESET: | ||
break; | ||
case USBD_EVENT_DISCONNECTED: | ||
for (size_t i = 0; i < 8; i++) { | ||
if (g_usb_cdcacm_serial_ep[0][i & 0x0f].used) { | ||
g_usb_cdcacm_serial_ep[0][i & 0x0f].error = -ESHUTDOWN; | ||
nxsem_post(&g_usb_cdcacm_serial_ep[0][i & 0x0f].rxdone_sem); | ||
} | ||
if (g_usb_cdcacm_serial_ep[1][i & 0x0f].used) { | ||
g_usb_cdcacm_serial_ep[1][i & 0x0f].error = -ESHUTDOWN; | ||
nxsem_post(&g_usb_cdcacm_serial_ep[1][i & 0x0f].txdone_sem); | ||
} | ||
} | ||
break; | ||
case USBD_EVENT_CONFIGURED: | ||
for (size_t i = 0; i < 8; i++) { | ||
if (g_usb_cdcacm_serial_ep[0][i & 0x0f].used) { | ||
g_usb_cdcacm_serial_ep[0][i & 0x0f].error = 0; | ||
nxsem_post(&g_usb_cdcacm_serial_ep[0][i & 0x0f].rxdone_sem); | ||
} | ||
if (g_usb_cdcacm_serial_ep[1][i & 0x0f].used) { | ||
g_usb_cdcacm_serial_ep[1][i & 0x0f].error = 0; | ||
nxsem_post(&g_usb_cdcacm_serial_ep[1][i & 0x0f].txdone_sem); | ||
} | ||
} | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
|
||
void usbd_cdcacm_init(uint8_t busid, uint8_t id, const char *path, uint8_t outep, uint8_t inep) | ||
{ | ||
g_usb_cdcacm_serial[id] = kmm_malloc(sizeof(struct usbdev_serial_s)); | ||
DEBUGASSERT(g_usb_cdcacm_serial[id]); | ||
|
||
memset(g_usb_cdcacm_serial[id], 0, sizeof(struct usbdev_serial_s)); | ||
strncpy(g_usb_cdcacm_serial[id]->name, path, sizeof(g_usb_cdcacm_serial[id]->name) - 1); | ||
|
||
circbuf_init(&g_usb_cdcacm_serial[id]->circ, g_usb_cdcacm_serial[id]->cache_rxbuffer, CONFIG_USBDEV_CDCACM_RXBUFSIZE); | ||
|
||
nxsem_init(&g_usb_cdcacm_serial_ep[0][outep & 0x0f].rxdone_sem, 0, 0); | ||
nxsem_init(&g_usb_cdcacm_serial_ep[1][inep & 0x0f].txdone_sem, 0, 0); | ||
g_usb_cdcacm_serial_ep[0][outep & 0x0f].used = true; | ||
g_usb_cdcacm_serial_ep[1][inep & 0x0f].used = true; | ||
|
||
usbd_add_interface(busid, usbd_cdc_acm_init_intf(busid, &g_usb_cdcacm_serial[id]->ctrl_intf)); | ||
usbd_add_interface(busid, usbd_cdc_acm_init_intf(busid, &g_usb_cdcacm_serial[id]->data_intf)); | ||
g_usb_cdcacm_serial[id]->ctrl_intf.notify_handler = cdcacm_notify_handler; | ||
g_usb_cdcacm_serial[id]->outep = outep; | ||
g_usb_cdcacm_serial[id]->inep = inep; | ||
|
||
cdc_out_ep[id].ep_addr = outep; | ||
cdc_out_ep[id].ep_cb = usbd_cdc_acm_bulk_out; | ||
cdc_in_ep[id].ep_addr = inep; | ||
cdc_in_ep[id].ep_cb = usbd_cdc_acm_bulk_in; | ||
|
||
usbd_add_endpoint(busid, &cdc_out_ep[id]); | ||
usbd_add_endpoint(busid, &cdc_in_ep[id]); | ||
|
||
register_driver(path, &g_usbdevops, 0666, g_usb_cdcacm_serial[id]); | ||
} | ||
|
||
void usbd_cdcacm_deinit(uint8_t busid, uint8_t id) | ||
{ | ||
unregister_driver(g_usb_cdcacm_serial[id]->name); | ||
|
||
kmm_free(g_usb_cdcacm_serial[id]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* | ||
* Copyright (c) 2025, sakumisu | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
#include <nuttx/fs/fs.h> | ||
|
||
#include "usbd_core.h" | ||
#include "usbd_msc.h" | ||
|
||
#ifndef CONFIG_USBDEV_MSC_THREAD | ||
#error "CONFIG_USBDEV_MSC_THREAD must be enabled" | ||
#endif | ||
|
||
static FAR struct inode *inode; | ||
static struct geometry geo; | ||
static char devpath[32]; | ||
|
||
void usbd_msc_get_cap(uint8_t busid, uint8_t lun, uint32_t *block_num, uint32_t *block_size) | ||
{ | ||
int ret; | ||
|
||
/* Open the block driver */ | ||
|
||
ret = open_blockdriver(devpath, 0, &inode); | ||
if (ret < 0) { | ||
*block_num = 0; | ||
*block_size = 0; | ||
return; | ||
} | ||
|
||
/* Get the drive geometry */ | ||
|
||
if (!inode || !inode->u.i_bops || !inode->u.i_bops->geometry || | ||
inode->u.i_bops->geometry(inode, &geo) != OK || !geo.geo_available) { | ||
*block_num = 0; | ||
*block_size = 0; | ||
return; | ||
} | ||
|
||
*block_num = geo.geo_nsectors; | ||
*block_size = geo.geo_sectorsize; | ||
|
||
USB_LOG_INFO("block_num: %ld, block_size: %ld\n", *block_num, *block_size); | ||
} | ||
|
||
int usbd_msc_sector_read(uint8_t busid, uint8_t lun, uint32_t sector, uint8_t *buffer, uint32_t length) | ||
{ | ||
if (inode->u.i_bops->read) { | ||
inode->u.i_bops->read(inode, buffer, sector, length / geo.geo_sectorsize); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
int usbd_msc_sector_write(uint8_t busid, uint8_t lun, uint32_t sector, uint8_t *buffer, uint32_t length) | ||
{ | ||
if (inode->u.i_bops->write) { | ||
inode->u.i_bops->write(inode, buffer, sector, length / geo.geo_sectorsize); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static struct usbd_interface intf0; | ||
|
||
void usbd_msc_init(uint8_t busid, char *path, uint8_t outep, uint8_t inep) | ||
{ | ||
memset(devpath, 0, sizeof(devpath)); | ||
strncpy(devpath, path, sizeof(devpath) - 1); | ||
usbd_add_interface(busid, usbd_msc_init_intf(busid, &intf0, outep, inep)); | ||
} |
Oops, something went wrong.