2025-12-12 22:45:10 +08:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2025, sakumisu
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
|
|
|
|
#include <rtthread.h>
|
|
|
|
|
#include <rtdevice.h>
|
|
|
|
|
|
|
|
|
|
#include "usbh_core.h"
|
|
|
|
|
#include "usbh_serial.h"
|
|
|
|
|
|
|
|
|
|
static rt_err_t rt_usbh_serial_open(struct rt_device *dev, rt_uint16_t oflag)
|
|
|
|
|
{
|
|
|
|
|
struct usbh_serial *serial;
|
|
|
|
|
|
|
|
|
|
RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
|
|
|
|
|
|
|
|
|
|
serial = (struct usbh_serial *)dev->user_data;
|
|
|
|
|
|
|
|
|
|
serial = usbh_serial_open(serial->hport->config.intf[serial->intf].devname, USBH_SERIAL_O_RDWR | USBH_SERIAL_O_NONBLOCK);
|
|
|
|
|
if (serial == RT_NULL) {
|
|
|
|
|
USB_LOG_ERR("serial open failed\n");
|
|
|
|
|
return -RT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct usbh_serial_termios termios;
|
|
|
|
|
|
|
|
|
|
memset(&termios, 0, sizeof(termios));
|
|
|
|
|
termios.baudrate = 115200;
|
|
|
|
|
termios.stopbits = 0;
|
|
|
|
|
termios.parity = 0;
|
|
|
|
|
termios.databits = 8;
|
|
|
|
|
termios.rtscts = false;
|
|
|
|
|
termios.rx_timeout = 0;
|
|
|
|
|
|
|
|
|
|
usbh_serial_control(serial, USBH_SERIAL_CMD_SET_ATTR, &termios);
|
|
|
|
|
|
|
|
|
|
return RT_EOK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static rt_err_t rt_usbh_serial_close(struct rt_device *dev)
|
|
|
|
|
{
|
|
|
|
|
struct usbh_serial *serial;
|
|
|
|
|
|
|
|
|
|
RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
|
|
|
|
|
|
|
|
|
|
serial = (struct usbh_serial *)dev->user_data;
|
|
|
|
|
|
|
|
|
|
usbh_serial_close(serial);
|
|
|
|
|
|
|
|
|
|
return RT_EOK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static rt_ssize_t rt_usbh_serial_read(struct rt_device *dev,
|
|
|
|
|
rt_off_t pos,
|
|
|
|
|
void *buffer,
|
|
|
|
|
rt_size_t size)
|
|
|
|
|
{
|
|
|
|
|
struct usbh_serial *serial;
|
|
|
|
|
|
|
|
|
|
RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
|
|
|
|
|
|
|
|
|
|
serial = (struct usbh_serial *)dev->user_data;
|
|
|
|
|
|
|
|
|
|
return usbh_serial_read(serial, buffer, size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static rt_ssize_t rt_usbh_serial_write(struct rt_device *dev,
|
|
|
|
|
rt_off_t pos,
|
|
|
|
|
const void *buffer,
|
|
|
|
|
rt_size_t size)
|
|
|
|
|
{
|
|
|
|
|
struct usbh_serial *serial;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
rt_uint8_t *align_buf;
|
|
|
|
|
|
|
|
|
|
RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
|
|
|
|
|
|
|
|
|
|
serial = (struct usbh_serial *)dev->user_data;
|
|
|
|
|
|
|
|
|
|
align_buf = (rt_uint8_t *)buffer;
|
|
|
|
|
|
|
|
|
|
if ((uint32_t)buffer & (CONFIG_USB_ALIGN_SIZE - 1)) {
|
|
|
|
|
align_buf = rt_malloc_align(USB_ALIGN_UP(size, CONFIG_USB_ALIGN_SIZE), CONFIG_USB_ALIGN_SIZE);
|
|
|
|
|
if (!align_buf) {
|
|
|
|
|
USB_LOG_ERR("serial get align buf failed\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
usb_memcpy(align_buf, buffer, size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = usbh_serial_write(serial, align_buf, size);
|
|
|
|
|
|
|
|
|
|
if ((uint32_t)buffer & (CONFIG_USB_ALIGN_SIZE - 1)) {
|
|
|
|
|
rt_free_align(align_buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static rt_err_t rt_usbh_serial_control(struct rt_device *dev,
|
|
|
|
|
int cmd,
|
|
|
|
|
void *args)
|
|
|
|
|
{
|
|
|
|
|
struct usbh_serial *serial;
|
|
|
|
|
struct serial_configure *config;
|
|
|
|
|
int ret = -RT_EINVAL;
|
|
|
|
|
|
|
|
|
|
RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
|
|
|
|
|
|
|
|
|
|
serial = (struct usbh_serial *)dev->user_data;
|
|
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
|
case RT_DEVICE_CTRL_CONFIG: {
|
|
|
|
|
config = (struct serial_configure *)args;
|
|
|
|
|
|
|
|
|
|
struct usbh_serial_termios termios;
|
|
|
|
|
|
|
|
|
|
memset(&termios, 0, sizeof(termios));
|
|
|
|
|
termios.baudrate = config->baud_rate;
|
|
|
|
|
termios.stopbits = 0;
|
|
|
|
|
termios.parity = config->parity;
|
|
|
|
|
termios.databits = config->data_bits;
|
|
|
|
|
termios.rtscts = false;
|
|
|
|
|
termios.rx_timeout = 0;
|
|
|
|
|
|
|
|
|
|
usbh_serial_control(serial, USBH_SERIAL_CMD_SET_ATTR, &termios);
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef RT_USING_DEVICE_OPS
|
|
|
|
|
const static struct rt_device_ops usbh_serial_ops = {
|
|
|
|
|
NULL,
|
|
|
|
|
rt_usbh_serial_open,
|
|
|
|
|
rt_usbh_serial_close,
|
|
|
|
|
rt_usbh_serial_read,
|
|
|
|
|
rt_usbh_serial_write,
|
|
|
|
|
rt_usbh_serial_control
|
|
|
|
|
};
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
rt_err_t usbh_serial_register(struct usbh_serial *serial)
|
|
|
|
|
{
|
|
|
|
|
rt_err_t ret;
|
|
|
|
|
struct rt_device *device;
|
|
|
|
|
RT_ASSERT(serial != RT_NULL);
|
|
|
|
|
|
|
|
|
|
device = rt_malloc(sizeof(struct rt_device));
|
|
|
|
|
if (device == RT_NULL) {
|
|
|
|
|
USB_LOG_ERR("serial device malloc failed\n");
|
|
|
|
|
return -RT_ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
memset(device, 0, sizeof(struct rt_device));
|
|
|
|
|
|
|
|
|
|
device->type = RT_Device_Class_Char;
|
|
|
|
|
device->rx_indicate = RT_NULL;
|
|
|
|
|
device->tx_complete = RT_NULL;
|
|
|
|
|
|
|
|
|
|
#ifdef RT_USING_DEVICE_OPS
|
|
|
|
|
device->ops = &usbh_serial_ops;
|
|
|
|
|
#else
|
|
|
|
|
device->init = NULL;
|
|
|
|
|
device->open = rt_usbh_serial_open;
|
|
|
|
|
device->close = rt_usbh_serial_close;
|
|
|
|
|
device->read = rt_usbh_serial_read;
|
|
|
|
|
device->write = rt_usbh_serial_write;
|
|
|
|
|
device->control = rt_usbh_serial_control;
|
|
|
|
|
#endif
|
|
|
|
|
device->user_data = serial;
|
|
|
|
|
serial->user_data = device;
|
|
|
|
|
|
2025-12-15 00:34:17 +08:00
|
|
|
/* skip /dev/ to avoid BAD file */
|
|
|
|
|
const char *dev_name = serial->hport->config.intf[serial->intf].devname;
|
|
|
|
|
if (strncmp(dev_name, "/dev/", 5) == 0) {
|
|
|
|
|
dev_name += 5;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-12 22:45:10 +08:00
|
|
|
/* register a character device */
|
2025-12-15 00:34:17 +08:00
|
|
|
ret = rt_device_register(device, dev_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_REMOVABLE);
|
2025-12-12 22:45:10 +08:00
|
|
|
|
|
|
|
|
#ifdef RT_USING_POSIX_DEVIO
|
|
|
|
|
/* set fops */
|
|
|
|
|
device->fops = &usbh_serial_fops;
|
|
|
|
|
#endif
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void usbh_serial_unregister(struct usbh_serial *serial)
|
|
|
|
|
{
|
|
|
|
|
struct rt_device *device;
|
|
|
|
|
|
|
|
|
|
RT_ASSERT(serial != NULL && serial->user_data != NULL);
|
|
|
|
|
|
|
|
|
|
device = (struct rt_device *)serial->user_data;
|
|
|
|
|
|
|
|
|
|
rt_device_unregister(device);
|
|
|
|
|
rt_free(device);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void usbh_serial_run(struct usbh_serial *serial)
|
|
|
|
|
{
|
|
|
|
|
usbh_serial_register(serial);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void usbh_serial_stop(struct usbh_serial *serial)
|
|
|
|
|
{
|
|
|
|
|
usbh_serial_unregister(serial);
|
|
|
|
|
}
|