mirror of
https://github.com/openharmony/drivers_adapter_khdf_linux.git
synced 2026-07-21 20:15:23 -04:00
73cba3f367
Signed-off-by: mahai <mahai5@huawei.com>
536 lines
14 KiB
C
536 lines
14 KiB
C
/*
|
|
* uart_adapter.c
|
|
*
|
|
* linux uart driver adapter.
|
|
*
|
|
* Copyright (c) 2020-2021 Huawei Device Co., Ltd.
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#include <linux/termios.h>
|
|
#include <asm/ioctls.h>
|
|
#include <linux/serial.h>
|
|
#include <linux/fs.h>
|
|
#include "device_resource_if.h"
|
|
#include "hdf_base.h"
|
|
#include "hdf_log.h"
|
|
#include "osal_io.h"
|
|
#include "osal_mem.h"
|
|
#include "osal_time.h"
|
|
#include "securec.h"
|
|
#include "uart_if.h"
|
|
#include "uart_core.h"
|
|
|
|
#define HDF_LOG_TAG hdf_uart_adapter
|
|
#define UART_NAME_LEN 20
|
|
#define UART_PATHNAME_LEN (UART_NAME_LEN + 15)
|
|
|
|
static char g_driverName[UART_NAME_LEN];
|
|
|
|
static int32_t UartAdapterInit(struct UartHost *host)
|
|
{
|
|
char name[UART_PATHNAME_LEN] = {0};
|
|
struct file *fp = NULL;
|
|
mm_segment_t oldfs;
|
|
|
|
if (host == NULL) {
|
|
return HDF_ERR_INVALID_OBJECT;
|
|
}
|
|
if (sprintf_s(name, UART_PATHNAME_LEN - 1, "/dev/%s%d", g_driverName, host->num) < 0) {
|
|
return HDF_FAILURE;
|
|
}
|
|
oldfs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
fp = filp_open(name, O_RDWR | O_NOCTTY | O_NDELAY, 0600); /* 0600 : file mode */
|
|
if (IS_ERR(fp)) {
|
|
HDF_LOGE("filp_open %s fail", name);
|
|
set_fs(oldfs);
|
|
return HDF_FAILURE;
|
|
}
|
|
set_fs(oldfs);
|
|
host->priv = fp;
|
|
return HDF_SUCCESS;
|
|
}
|
|
static int32_t UartAdapterDeInit(struct UartHost *host)
|
|
{
|
|
int32_t ret = HDF_SUCCESS;
|
|
struct file *fp = NULL;
|
|
mm_segment_t oldfs;
|
|
|
|
if (host == NULL) {
|
|
return HDF_ERR_INVALID_OBJECT;
|
|
}
|
|
fp = (struct file *)host->priv;
|
|
oldfs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
if (!IS_ERR(fp) && fp) {
|
|
ret = filp_close(fp, NULL);
|
|
}
|
|
set_fs(oldfs);
|
|
return ret;
|
|
}
|
|
static int32_t UartAdapterRead(struct UartHost *host, uint8_t *data, uint32_t size)
|
|
{
|
|
loff_t pos = 0;
|
|
int ret;
|
|
struct file *fp = NULL;
|
|
char __user *p = (__force char __user *)data;
|
|
mm_segment_t oldfs;
|
|
uint32_t tmp = 0;
|
|
|
|
if (host == NULL) {
|
|
return HDF_ERR_INVALID_OBJECT;
|
|
}
|
|
fp = (struct file *)host->priv;
|
|
if (data == NULL || size == 0) {
|
|
return HDF_ERR_INVALID_PARAM;
|
|
}
|
|
oldfs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
while (size >= tmp) {
|
|
ret = vfs_read(fp, p + tmp, 1, &pos);
|
|
if (ret < 0) {
|
|
HDF_LOGE("vfs_read fail %d", ret);
|
|
break;
|
|
}
|
|
tmp++;
|
|
}
|
|
set_fs(oldfs);
|
|
return tmp;
|
|
}
|
|
|
|
static int32_t UartAdapterWrite(struct UartHost *host, uint8_t *data, uint32_t size)
|
|
{
|
|
loff_t pos = 0;
|
|
int ret;
|
|
struct file *fp = NULL;
|
|
char __user *p = (__force char __user *)data;
|
|
mm_segment_t oldfs;
|
|
|
|
if (host == NULL) {
|
|
return HDF_ERR_INVALID_OBJECT;
|
|
}
|
|
fp = (struct file *)host->priv;
|
|
if (data == NULL || size == 0) {
|
|
return HDF_ERR_INVALID_PARAM;
|
|
}
|
|
oldfs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
ret = vfs_write(fp, p, size, &pos);
|
|
if (ret < 0) {
|
|
HDF_LOGE("vfs_write fail %d", ret);
|
|
set_fs(oldfs);
|
|
return HDF_FAILURE;
|
|
}
|
|
set_fs(oldfs);
|
|
return HDF_SUCCESS;
|
|
}
|
|
|
|
static int UartAdapterIoctlInner(struct file *fp, unsigned cmd, unsigned long arg)
|
|
{
|
|
int ret = HDF_FAILURE;
|
|
mm_segment_t oldfs;
|
|
|
|
oldfs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
if (fp->f_op->unlocked_ioctl) {
|
|
ret = fp->f_op->unlocked_ioctl(fp, cmd, arg);
|
|
}
|
|
set_fs(oldfs);
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t CflagToBaudRate(unsigned short flag)
|
|
{
|
|
uint32_t baud;
|
|
|
|
switch ((flag | CBAUD)) {
|
|
case B1800:
|
|
baud = 1800; /* 1800 : baudrate */
|
|
break;
|
|
case B2400:
|
|
baud = 2400; /* 2400 : baudrate */
|
|
break;
|
|
case B4800:
|
|
baud = 4800; /* 4800 : baudrate */
|
|
break;
|
|
case B9600:
|
|
baud = 9600; /* 9600 : baudrate */
|
|
break;
|
|
case B19200:
|
|
baud = 19200; /* 19200 : baudrate */
|
|
break;
|
|
case B38400:
|
|
baud = 38400; /* 38400 : baudrate */
|
|
break;
|
|
case B57600:
|
|
baud = 57600; /* 57600 : baudrate */
|
|
break;
|
|
case B115200:
|
|
baud = 115200; /* 115200 : baudrate */
|
|
break;
|
|
case B230400:
|
|
baud = 230400; /* 230400: baudrate */
|
|
break;
|
|
case B460800:
|
|
baud = 460800; /* 460800: baudrate */
|
|
break;
|
|
case B500000:
|
|
baud = 500000; /* 500000: baudrate */
|
|
break;
|
|
case B576000:
|
|
baud = 576000; /* 576000: baudrate */
|
|
break;
|
|
case B921600:
|
|
baud = 921600; /* 921600: baudrate */
|
|
break;
|
|
default:
|
|
baud = 9600; /* 9600 : baudrate on default */
|
|
break;
|
|
}
|
|
return baud;
|
|
}
|
|
|
|
static int32_t UartAdapterGetBaud(struct UartHost *host, uint32_t *baudRate)
|
|
{
|
|
struct termios termios;
|
|
struct file *fp = NULL;
|
|
|
|
if (host == NULL) {
|
|
return HDF_ERR_INVALID_OBJECT;
|
|
}
|
|
fp = (struct file *)host->priv;
|
|
if (baudRate == NULL) {
|
|
return HDF_ERR_INVALID_PARAM;
|
|
}
|
|
if (UartAdapterIoctlInner(fp, TCGETS, (unsigned long)&termios) < 0) {
|
|
HDF_LOGE("tcgets fail");
|
|
return HDF_FAILURE;
|
|
}
|
|
*baudRate = CflagToBaudRate(termios.c_cflag);
|
|
return HDF_SUCCESS;
|
|
}
|
|
|
|
static unsigned short BaudRateToCflag(uint32_t baudRate)
|
|
{
|
|
unsigned short ret;
|
|
switch (baudRate) {
|
|
case 1800: /* 1800 : baudrate */
|
|
ret = B1800;
|
|
break;
|
|
case 2400: /* 2400 : baudrate */
|
|
ret = B2400;
|
|
break;
|
|
case 4800: /* 4800 : baudrate */
|
|
ret = B4800;
|
|
break;
|
|
case 9600: /* 9600 : baudrate */
|
|
ret = B9600;
|
|
break;
|
|
case 19200: /* 19200 : baudrate */
|
|
ret = B19200;
|
|
break;
|
|
case 38400: /* 38400 : baudrate */
|
|
ret = B38400;
|
|
break;
|
|
case 57600: /* 57600 : baudrate */
|
|
ret = B57600;
|
|
break;
|
|
case 115200: /* 115200 : baudrate */
|
|
ret = B115200;
|
|
break;
|
|
case 230400: /* 230400 : baudrate */
|
|
ret = B230400;
|
|
break;
|
|
case 460800: /* 460800 : baudrate */
|
|
ret = B460800;
|
|
break;
|
|
case 500000: /* 500000 : baudrate */
|
|
ret = B500000;
|
|
break;
|
|
case 576000: /* 576000 : baudrate */
|
|
ret = B576000;
|
|
break;
|
|
case 921600: /* 921600 : baudrate */
|
|
ret = B921600;
|
|
break;
|
|
default:
|
|
ret = B9600;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int32_t UartAdapterSetBaud(struct UartHost *host, uint32_t baudRate)
|
|
{
|
|
struct termios termios;
|
|
struct serial_struct serial;
|
|
struct file *fp = NULL;
|
|
int ret;
|
|
|
|
if (host == NULL) {
|
|
return HDF_ERR_INVALID_OBJECT;
|
|
}
|
|
fp = (struct file *)host->priv;
|
|
|
|
if (UartAdapterIoctlInner(fp, TCGETS, (unsigned long)&termios) < 0) {
|
|
HDF_LOGE("tcgets fail");
|
|
return HDF_FAILURE;
|
|
}
|
|
termios.c_cflag &= ~CBAUD;
|
|
termios.c_cflag |= BaudRateToCflag(baudRate);
|
|
termios.c_cc[VMIN] = 0;
|
|
termios.c_cc[VTIME] = 0;
|
|
ret = UartAdapterIoctlInner(fp, TCSETS, (unsigned long)&termios);
|
|
/* Set low latency */
|
|
if (UartAdapterIoctlInner(fp, TIOCGSERIAL, (unsigned long)&serial) < 0) {
|
|
HDF_LOGE("tiocgserial fail");
|
|
return HDF_FAILURE;
|
|
}
|
|
serial.flags |= ASYNC_LOW_LATENCY;
|
|
ret = UartAdapterIoctlInner(fp, TIOCSSERIAL, (unsigned long)&serial);
|
|
return ret;
|
|
}
|
|
|
|
static unsigned char CSToAttr(unsigned short cs)
|
|
{
|
|
unsigned short t = cs & ~CSIZE;
|
|
if (t == CS7) {
|
|
return UART_ATTR_DATABIT_7;
|
|
} else if (t == CS8) {
|
|
return UART_ATTR_DATABIT_8;
|
|
} else if (t == CS6) {
|
|
return UART_ATTR_DATABIT_6;
|
|
} else if (t == CS5) {
|
|
return UART_ATTR_DATABIT_5;
|
|
} else {
|
|
/* default value */
|
|
return UART_ATTR_DATABIT_8;
|
|
}
|
|
}
|
|
|
|
static unsigned short AttrToCs(unsigned char attr)
|
|
{
|
|
if (attr == UART_ATTR_DATABIT_7) {
|
|
return CS7;
|
|
} else if (attr == UART_ATTR_DATABIT_8) {
|
|
return CS8;
|
|
} else if (attr == UART_ATTR_DATABIT_6) {
|
|
return CS6;
|
|
} else if (attr == UART_ATTR_DATABIT_5) {
|
|
return CS5;
|
|
} else {
|
|
/* default value */
|
|
return CS8;
|
|
}
|
|
}
|
|
|
|
static unsigned char PariTyToAttr(unsigned short ps)
|
|
{
|
|
if (ps & (PARENB | PARODD)) {
|
|
return UART_ATTR_PARITY_ODD;
|
|
} else if (!(ps & PARODD) && (ps & PARENB)) {
|
|
return UART_ATTR_PARITY_EVEN;
|
|
} else if (!(ps & (PARENB | PARODD))) {
|
|
return UART_ATTR_PARITY_NONE;
|
|
} else {
|
|
/* default value */
|
|
return UART_ATTR_PARITY_NONE;
|
|
}
|
|
}
|
|
|
|
static unsigned char StopBitToAttr(unsigned short st)
|
|
{
|
|
if (!(st & CSTOPB)) {
|
|
return UART_ATTR_STOPBIT_1;
|
|
} else if (st & CSTOPB) {
|
|
return UART_ATTR_STOPBIT_2;
|
|
} else {
|
|
/* default value */
|
|
return UART_ATTR_STOPBIT_1;
|
|
}
|
|
}
|
|
|
|
static unsigned char CtsRtsToAttr(unsigned short cr)
|
|
{
|
|
if (cr & CRTSCTS) {
|
|
return UART_ATTR_RTS_EN;
|
|
}
|
|
return UART_ATTR_RTS_DIS;
|
|
}
|
|
|
|
static int32_t UartAdapterGetAttribute(struct UartHost *host, struct UartAttribute *attribute)
|
|
{
|
|
struct termios termios;
|
|
struct file *fp = NULL;
|
|
int ret;
|
|
|
|
if (host == NULL) {
|
|
return HDF_ERR_INVALID_OBJECT;
|
|
}
|
|
fp = (struct file *)host->priv;
|
|
if (attribute == NULL) {
|
|
return HDF_ERR_INVALID_PARAM;
|
|
}
|
|
ret = UartAdapterIoctlInner(fp, TCGETS, (unsigned long)&termios);
|
|
if (ret < 0) {
|
|
return HDF_FAILURE;
|
|
}
|
|
attribute->dataBits = CSToAttr(termios.c_cflag);
|
|
attribute->parity = PariTyToAttr(termios.c_cflag);
|
|
attribute->stopBits = StopBitToAttr(termios.c_cflag);
|
|
attribute->cts = CtsRtsToAttr(termios.c_cflag);
|
|
attribute->rts = CtsRtsToAttr(termios.c_cflag);
|
|
return HDF_SUCCESS;
|
|
}
|
|
static int32_t UartAdapterSetAttribute(struct UartHost *host, struct UartAttribute *attribute)
|
|
{
|
|
struct termios termios;
|
|
struct file *fp = NULL;
|
|
int ret;
|
|
|
|
if (host == NULL) {
|
|
return HDF_ERR_INVALID_OBJECT;
|
|
}
|
|
fp = (struct file *)host->priv;
|
|
if (attribute == NULL) {
|
|
return HDF_ERR_INVALID_PARAM;
|
|
}
|
|
ret = UartAdapterIoctlInner(fp, TCGETS, (unsigned long)&termios);
|
|
if (ret < 0) {
|
|
return HDF_FAILURE;
|
|
}
|
|
termios.c_cflag |= CLOCAL | CREAD;
|
|
termios.c_cflag &= ~CSIZE;
|
|
termios.c_cflag |= AttrToCs(attribute->dataBits);
|
|
if (attribute->cts || attribute->rts) {
|
|
termios.c_cflag |= CRTSCTS;
|
|
} else {
|
|
termios.c_cflag &= ~CRTSCTS;
|
|
}
|
|
if (attribute->parity == UART_ATTR_PARITY_ODD) {
|
|
termios.c_cflag |= (PARODD | PARENB);
|
|
} else if (attribute->parity == UART_ATTR_PARITY_EVEN) {
|
|
termios.c_cflag |= PARENB;
|
|
termios.c_cflag &= ~PARODD;
|
|
} else if (attribute->parity == UART_ATTR_PARITY_NONE) {
|
|
termios.c_cflag &= ~(PARENB | PARODD);
|
|
} else { /* default value */
|
|
termios.c_cflag &= ~(PARENB | PARODD);
|
|
}
|
|
if (attribute->stopBits == UART_ATTR_STOPBIT_1) {
|
|
termios.c_cflag &= ~CSTOPB;
|
|
} else if (attribute->stopBits == UART_ATTR_STOPBIT_2) {
|
|
termios.c_cflag |= CSTOPB;
|
|
} else {
|
|
/* default value */
|
|
termios.c_cflag &= ~CSTOPB;
|
|
}
|
|
ret = UartAdapterIoctlInner(fp, TCSETS, (unsigned long)&termios);
|
|
return ret;
|
|
}
|
|
|
|
static int32_t UartAdapterSetTransMode(struct UartHost *host, enum UartTransMode mode)
|
|
{
|
|
(void)host;
|
|
(void)mode;
|
|
return HDF_SUCCESS;
|
|
}
|
|
|
|
static struct UartHostMethod g_uartHostMethod = {
|
|
.Init = UartAdapterInit,
|
|
.Deinit = UartAdapterDeInit,
|
|
.Read = UartAdapterRead,
|
|
.Write = UartAdapterWrite,
|
|
.SetBaud = UartAdapterSetBaud,
|
|
.GetBaud = UartAdapterGetBaud,
|
|
.SetAttribute = UartAdapterSetAttribute,
|
|
.GetAttribute = UartAdapterGetAttribute,
|
|
.SetTransMode = UartAdapterSetTransMode,
|
|
};
|
|
|
|
static int32_t HdfUartBind(struct HdfDeviceObject *obj)
|
|
{
|
|
HDF_LOGI("%s: entry", __func__);
|
|
if (obj == NULL) {
|
|
return HDF_ERR_INVALID_OBJECT;
|
|
}
|
|
return (UartHostCreate(obj) == NULL) ? HDF_FAILURE : HDF_SUCCESS;
|
|
}
|
|
|
|
static int32_t HdfUartInit(struct HdfDeviceObject *obj)
|
|
{
|
|
int32_t ret;
|
|
struct DeviceResourceIface *iface = NULL;
|
|
struct UartHost *host = NULL;
|
|
const char *drName = NULL;
|
|
|
|
HDF_LOGI("%s: entry", __func__);
|
|
if (obj == NULL) {
|
|
HDF_LOGE("%s: device is null", __func__);
|
|
return HDF_ERR_INVALID_OBJECT;
|
|
}
|
|
host = UartHostFromDevice(obj);
|
|
if (host == NULL) {
|
|
HDF_LOGE("%s: host is null", __func__);
|
|
return HDF_FAILURE;
|
|
}
|
|
iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
|
|
if (iface == NULL || iface->GetUint32 == NULL) {
|
|
HDF_LOGE("%s: face is invalid", __func__);
|
|
return HDF_FAILURE;
|
|
}
|
|
|
|
if (iface->GetUint32(obj->property, "num", &host->num, 0) != HDF_SUCCESS) {
|
|
HDF_LOGE("%s: read num fail", __func__);
|
|
return HDF_FAILURE;
|
|
}
|
|
if (iface->GetString(obj->property, "driver_name", &drName, "ttyAMA") != HDF_SUCCESS) {
|
|
HDF_LOGE("%s: read driver_name fail", __func__);
|
|
return HDF_FAILURE;
|
|
}
|
|
g_driverName[UART_NAME_LEN - 1] = 0;
|
|
if (strlen(drName) > (UART_NAME_LEN - 1)) {
|
|
HDF_LOGE("%s: Illegal length of drName", __func__);
|
|
return HDF_FAILURE;
|
|
}
|
|
ret = memcpy_s(g_driverName, UART_NAME_LEN, drName, strlen(drName));
|
|
if (ret != EOK) {
|
|
return HDF_FAILURE;
|
|
}
|
|
host->method = &g_uartHostMethod;
|
|
return HDF_SUCCESS;
|
|
}
|
|
|
|
static void HdfUartRelease(struct HdfDeviceObject *obj)
|
|
{
|
|
struct UartHost *host = NULL;
|
|
|
|
HDF_LOGI("%s: entry", __func__);
|
|
if (obj == NULL) {
|
|
HDF_LOGE("%s: obj is null", __func__);
|
|
return;
|
|
}
|
|
host = UartHostFromDevice(obj);
|
|
UartHostDestroy(host);
|
|
}
|
|
|
|
struct HdfDriverEntry g_hdfUartchdog = {
|
|
.moduleVersion = 1,
|
|
.moduleName = "HDF_PLATFORM_UART",
|
|
.Bind = HdfUartBind,
|
|
.Init = HdfUartInit,
|
|
.Release = HdfUartRelease,
|
|
};
|
|
|
|
HDF_INIT(g_hdfUartchdog);
|