1710 lines
45 KiB
C
1710 lines
45 KiB
C
/*
|
|
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
|
|
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
* conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
* of conditions and the following disclaimer in the documentation and/or other materials
|
|
* provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
|
|
* to endorse or promote products derived from this software without specific prior written
|
|
* permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "console.h"
|
|
#include "fcntl.h"
|
|
#include "sys/ioctl.h"
|
|
#ifdef LOSCFG_FILE_MODE
|
|
#include "stdarg.h"
|
|
#endif
|
|
#include "unistd.h"
|
|
#include "securec.h"
|
|
#ifdef LOSCFG_SHELL_DMESG
|
|
#include "dmesg_pri.h"
|
|
#endif
|
|
#ifdef LOSCFG_SHELL
|
|
#include "shcmd.h"
|
|
#include "shell_pri.h"
|
|
#endif
|
|
#include "los_exc_pri.h"
|
|
#include "los_process_pri.h"
|
|
#include "los_sched_pri.h"
|
|
#include "user_copy.h"
|
|
#include "fs/driver.h"
|
|
|
|
#define EACH_CHAR 1
|
|
#define UART_IOC_MAGIC 'u'
|
|
#define UART_CFG_ATTR _IOW(UART_IOC_MAGIC, 5, int)
|
|
#define UART_CFG_PRIVATE _IOW(UART_IOC_MAGIC, 6, int)
|
|
/* Inter-module variable */
|
|
extern UINT32 g_uart_fputc_en;
|
|
STATIC UINT32 ConsoleSendTask(UINTPTR param);
|
|
|
|
STATIC UINT8 g_taskConsoleIDArray[LOSCFG_BASE_CORE_TSK_LIMIT];
|
|
STATIC SPIN_LOCK_INIT(g_consoleSpin);
|
|
|
|
#define SHELL_ENTRYID_INVALID 0xFFFFFFFF
|
|
#define SHELL_TASK_PRIORITY 9
|
|
#define CONSOLE_CIRBUF_EVENT 0x02U
|
|
#define CONSOLE_SEND_TASK_EXIT 0x04U
|
|
#define CONSOLE_SEND_TASK_RUNNING 0x10U
|
|
|
|
CONSOLE_CB *g_console[CONSOLE_NUM];
|
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
|
|
|
/*
|
|
* acquire uart driver function and filep of /dev/console,
|
|
* then store uart driver function in *filepOps
|
|
* and store filep of /dev/console in *privFilep.
|
|
*/
|
|
INT32 GetFilepOps(const struct file *filep, struct file **privFilep, const struct file_operations_vfs **filepOps)
|
|
{
|
|
INT32 ret;
|
|
|
|
if ((filep == NULL) || (filep->f_vnode == NULL) || (filep->f_vnode->data == NULL)) {
|
|
ret = EINVAL;
|
|
goto ERROUT;
|
|
}
|
|
|
|
/* to find console device's filep(now it is *privFilep) throught i_private */
|
|
struct drv_data *drv = (struct drv_data *)filep->f_vnode->data;
|
|
*privFilep = (struct file *)drv->priv;
|
|
if (((*privFilep)->f_vnode == NULL) || ((*privFilep)->f_vnode->data == NULL)) {
|
|
ret = EINVAL;
|
|
goto ERROUT;
|
|
}
|
|
|
|
/* to find uart driver operation function throutht u.i_opss */
|
|
|
|
drv = (struct drv_data *)(*privFilep)->f_vnode->data;
|
|
|
|
*filepOps = (const struct file_operations_vfs *)drv->ops;
|
|
|
|
return ENOERR;
|
|
ERROUT:
|
|
set_errno(ret);
|
|
return VFS_ERROR;
|
|
}
|
|
|
|
INT32 ConsoleTcGetAttr(INT32 fd, struct termios *termios)
|
|
{
|
|
struct file *filep = NULL;
|
|
CONSOLE_CB *consoleCB = NULL;
|
|
|
|
INT32 ret = fs_getfilep(fd, &filep);
|
|
if (ret < 0) {
|
|
return -EPERM;
|
|
}
|
|
|
|
consoleCB = (CONSOLE_CB *)filep->f_priv;
|
|
if (consoleCB == NULL) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
(VOID)memcpy_s(termios, sizeof(struct termios), &consoleCB->consoleTermios, sizeof(struct termios));
|
|
return LOS_OK;
|
|
}
|
|
|
|
INT32 ConsoleTcSetAttr(INT32 fd, INT32 actions, const struct termios *termios)
|
|
{
|
|
struct file *filep = NULL;
|
|
CONSOLE_CB *consoleCB = NULL;
|
|
|
|
(VOID)actions;
|
|
|
|
INT32 ret = fs_getfilep(fd, &filep);
|
|
if (ret < 0) {
|
|
return -EPERM;
|
|
}
|
|
|
|
consoleCB = (CONSOLE_CB *)filep->f_priv;
|
|
if (consoleCB == NULL) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
(VOID)memcpy_s(&consoleCB->consoleTermios, sizeof(struct termios), termios, sizeof(struct termios));
|
|
return LOS_OK;
|
|
}
|
|
|
|
STATIC UINT32 ConsoleRefcountGet(const CONSOLE_CB *consoleCB)
|
|
{
|
|
return consoleCB->refCount;
|
|
}
|
|
|
|
STATIC VOID ConsoleRefcountSet(CONSOLE_CB *consoleCB, BOOL flag)
|
|
{
|
|
if (flag == TRUE) {
|
|
++(consoleCB->refCount);
|
|
} else {
|
|
--(consoleCB->refCount);
|
|
}
|
|
}
|
|
|
|
BOOL IsConsoleOccupied(const CONSOLE_CB *consoleCB)
|
|
{
|
|
if (ConsoleRefcountGet(consoleCB) != FALSE) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
STATIC INT32 ConsoleCtrlCaptureLine(CONSOLE_CB *consoleCB)
|
|
{
|
|
struct termios consoleTermios = {0};
|
|
UINT32 intSave;
|
|
|
|
LOS_SpinLockSave(&g_consoleSpin, &intSave);
|
|
(VOID)ConsoleTcGetAttr(consoleCB->fd, &consoleTermios);
|
|
consoleTermios.c_lflag |= ICANON | ECHO;
|
|
(VOID)ConsoleTcSetAttr(consoleCB->fd, 0, &consoleTermios);
|
|
LOS_SpinUnlockRestore(&g_consoleSpin, intSave);
|
|
|
|
return LOS_OK;
|
|
}
|
|
|
|
STATIC INT32 ConsoleCtrlCaptureChar(CONSOLE_CB *consoleCB)
|
|
{
|
|
struct termios consoleTermios = {0};
|
|
UINT32 intSave;
|
|
|
|
LOS_SpinLockSave(&g_consoleSpin, &intSave);
|
|
(VOID)ConsoleTcGetAttr(consoleCB->fd, &consoleTermios);
|
|
consoleTermios.c_lflag &= ~(ICANON | ECHO);
|
|
(VOID)ConsoleTcSetAttr(consoleCB->fd, 0, &consoleTermios);
|
|
LOS_SpinUnlockRestore(&g_consoleSpin, intSave);
|
|
|
|
return LOS_OK;
|
|
}
|
|
|
|
STATIC INT32 ConsoleCtrlRightsCapture(CONSOLE_CB *consoleCB)
|
|
{
|
|
(VOID)LOS_SemPend(consoleCB->consoleSem, LOS_WAIT_FOREVER);
|
|
if ((ConsoleRefcountGet(consoleCB) == 0) &&
|
|
(OsCurrTaskGet()->taskID != consoleCB->shellEntryId)) {
|
|
/* not 0:indicate that shellentry is in uart_read, suspend shellentry task directly */
|
|
(VOID)LOS_TaskSuspend(consoleCB->shellEntryId);
|
|
}
|
|
ConsoleRefcountSet(consoleCB, TRUE);
|
|
return LOS_OK;
|
|
}
|
|
|
|
STATIC INT32 ConsoleCtrlRightsRelease(CONSOLE_CB *consoleCB)
|
|
{
|
|
if (ConsoleRefcountGet(consoleCB) == 0) {
|
|
PRINT_ERR("console is free\n");
|
|
(VOID)LOS_SemPost(consoleCB->consoleSem);
|
|
return LOS_NOK;
|
|
} else {
|
|
ConsoleRefcountSet(consoleCB, FALSE);
|
|
if ((ConsoleRefcountGet(consoleCB) == 0) &&
|
|
(OsCurrTaskGet()->taskID != consoleCB->shellEntryId)) {
|
|
(VOID)LOS_TaskResume(consoleCB->shellEntryId);
|
|
}
|
|
}
|
|
(VOID)LOS_SemPost(consoleCB->consoleSem);
|
|
return LOS_OK;
|
|
}
|
|
|
|
STATIC CONSOLE_CB *OsGetConsoleByDevice(const CHAR *deviceName)
|
|
{
|
|
INT32 ret;
|
|
struct Vnode *vnode = NULL;
|
|
|
|
VnodeHold();
|
|
ret = VnodeLookup(deviceName, &vnode, 0);
|
|
VnodeDrop();
|
|
if (ret < 0) {
|
|
set_errno(EACCES);
|
|
return NULL;
|
|
}
|
|
|
|
if (g_console[CONSOLE_SERIAL - 1]->devVnode == vnode) {
|
|
return g_console[CONSOLE_SERIAL - 1];
|
|
} else if (g_console[CONSOLE_TELNET - 1]->devVnode == vnode) {
|
|
return g_console[CONSOLE_TELNET - 1];
|
|
} else {
|
|
set_errno(ENOENT);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
STATIC INT32 OsGetConsoleID(const CHAR *deviceName)
|
|
{
|
|
if ((deviceName != NULL) &&
|
|
(strlen(deviceName) == strlen(SERIAL)) &&
|
|
(!strncmp(deviceName, SERIAL, strlen(SERIAL)))) {
|
|
return CONSOLE_SERIAL;
|
|
}
|
|
#ifdef LOSCFG_NET_TELNET
|
|
else if ((deviceName != NULL) &&
|
|
(strlen(deviceName) == strlen(TELNET)) &&
|
|
(!strncmp(deviceName, TELNET, strlen(TELNET)))) {
|
|
return CONSOLE_TELNET;
|
|
}
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
STATIC INT32 OsConsoleFullpathToID(const CHAR *fullpath)
|
|
{
|
|
#define CONSOLE_SERIAL_1 "/dev/console1"
|
|
#define CONSOLE_TELNET_2 "/dev/console2"
|
|
|
|
size_t len;
|
|
|
|
if (fullpath == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
len = strlen(fullpath);
|
|
if ((len == strlen(CONSOLE_SERIAL_1)) &&
|
|
(!strncmp(fullpath, CONSOLE_SERIAL_1, strlen(CONSOLE_SERIAL_1)))) {
|
|
return CONSOLE_SERIAL;
|
|
}
|
|
#ifdef LOSCFG_NET_TELNET
|
|
else if ((len == strlen(CONSOLE_TELNET_2)) &&
|
|
(!strncmp(fullpath, CONSOLE_TELNET_2, strlen(CONSOLE_TELNET_2)))) {
|
|
return CONSOLE_TELNET;
|
|
}
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
STATIC BOOL ConsoleFifoEmpty(const CONSOLE_CB *console)
|
|
{
|
|
if (console->fifoOut == console->fifoIn) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
STATIC VOID ConsoleFifoClearup(CONSOLE_CB *console)
|
|
{
|
|
console->fifoOut = 0;
|
|
console->fifoIn = 0;
|
|
(VOID)memset_s(console->fifo, CONSOLE_FIFO_SIZE, 0, CONSOLE_FIFO_SIZE);
|
|
}
|
|
|
|
STATIC VOID ConsoleFifoLenUpdate(CONSOLE_CB *console)
|
|
{
|
|
console->currentLen = console->fifoIn - console->fifoOut;
|
|
}
|
|
|
|
STATIC INT32 ConsoleReadFifo(CHAR *buffer, CONSOLE_CB *console, size_t bufLen)
|
|
{
|
|
INT32 ret;
|
|
UINT32 readNum;
|
|
|
|
readNum = MIN(bufLen, console->currentLen);
|
|
ret = memcpy_s(buffer, bufLen, console->fifo + console->fifoOut, readNum);
|
|
if (ret != EOK) {
|
|
PRINTK("%s,%d memcpy_s failed\n", __FUNCTION__, __LINE__);
|
|
return -1;
|
|
}
|
|
console->fifoOut += readNum;
|
|
if (ConsoleFifoEmpty(console)) {
|
|
ConsoleFifoClearup(console);
|
|
}
|
|
ConsoleFifoLenUpdate(console);
|
|
return (INT32)readNum;
|
|
}
|
|
|
|
INT32 FilepOpen(struct file *filep, const struct file_operations_vfs *fops)
|
|
{
|
|
INT32 ret;
|
|
if (fops->open == NULL) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
/*
|
|
* adopt uart open function to open filep (filep is
|
|
* corresponding to filep of /dev/console)
|
|
*/
|
|
ret = fops->open(filep);
|
|
if (ret < 0) {
|
|
return -EPERM;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
STATIC INLINE VOID UserEndOfRead(CONSOLE_CB *consoleCB, struct file *filep,
|
|
const struct file_operations_vfs *fops)
|
|
{
|
|
CHAR ch;
|
|
if (consoleCB->consoleTermios.c_lflag & ECHO) {
|
|
ch = '\r';
|
|
(VOID)fops->write(filep, &ch, 1);
|
|
}
|
|
consoleCB->fifo[consoleCB->fifoIn++] = '\n';
|
|
consoleCB->fifo[consoleCB->fifoIn] = '\0';
|
|
consoleCB->currentLen = consoleCB->fifoIn;
|
|
}
|
|
|
|
enum {
|
|
STAT_NOMAL_KEY,
|
|
STAT_ESC_KEY,
|
|
STAT_MULTI_KEY
|
|
};
|
|
|
|
STATIC INT32 UserShellCheckUDRL(const CHAR ch, INT32 *lastTokenType)
|
|
{
|
|
INT32 ret = LOS_OK;
|
|
if (ch == 0x1b) { /* 0x1b: ESC */
|
|
*lastTokenType = STAT_ESC_KEY;
|
|
return ret;
|
|
} else if (ch == 0x5b) { /* 0x5b: first Key combination */
|
|
if (*lastTokenType == STAT_ESC_KEY) {
|
|
*lastTokenType = STAT_MULTI_KEY;
|
|
return ret;
|
|
}
|
|
} else if (ch == 0x41) { /* up */
|
|
if (*lastTokenType == STAT_MULTI_KEY) {
|
|
*lastTokenType = STAT_NOMAL_KEY;
|
|
return ret;
|
|
}
|
|
} else if (ch == 0x42) { /* down */
|
|
if (*lastTokenType == STAT_MULTI_KEY) {
|
|
*lastTokenType = STAT_NOMAL_KEY;
|
|
return ret;
|
|
}
|
|
} else if (ch == 0x43) { /* right */
|
|
if (*lastTokenType == STAT_MULTI_KEY) {
|
|
*lastTokenType = STAT_NOMAL_KEY;
|
|
return ret;
|
|
}
|
|
} else if (ch == 0x44) { /* left */
|
|
if (*lastTokenType == STAT_MULTI_KEY) {
|
|
*lastTokenType = STAT_NOMAL_KEY;
|
|
return ret;
|
|
}
|
|
}
|
|
return LOS_NOK;
|
|
}
|
|
|
|
STATIC INT32 IsNeedContinue(CONSOLE_CB *consoleCB, char ch, INT32 *lastTokenType)
|
|
{
|
|
if (((ch == '\b') && (consoleCB->consoleTermios.c_lflag & ECHO) && (ConsoleFifoEmpty(consoleCB))) ||
|
|
(UserShellCheckUDRL(ch, lastTokenType) == LOS_OK)) { /* parse the up/down/right/left key */
|
|
return LOS_NOK;
|
|
}
|
|
|
|
return LOS_OK;
|
|
}
|
|
|
|
STATIC VOID EchoToTerminal(CONSOLE_CB *consoleCB, struct file *filep, const struct file_operations_vfs *fops, char ch)
|
|
{
|
|
if (consoleCB->consoleTermios.c_lflag & ECHO) {
|
|
if (ch == '\b') {
|
|
(VOID)fops->write(filep, "\b \b", 3);
|
|
} else {
|
|
(VOID)fops->write(filep, &ch, EACH_CHAR);
|
|
}
|
|
}
|
|
}
|
|
|
|
STATIC VOID StoreReadChar(CONSOLE_CB *consoleCB, char ch, INT32 readcount)
|
|
{
|
|
/* 3, store read char len need to minus \b */
|
|
if ((readcount == EACH_CHAR) && (consoleCB->fifoIn <= (CONSOLE_FIFO_SIZE - 3))) {
|
|
if (ch == '\b') {
|
|
if (!ConsoleFifoEmpty(consoleCB)) {
|
|
consoleCB->fifo[--consoleCB->fifoIn] = '\0';
|
|
}
|
|
} else {
|
|
consoleCB->fifo[consoleCB->fifoIn] = (UINT8)ch;
|
|
consoleCB->fifoIn++;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID KillPgrp()
|
|
{
|
|
INT32 consoleId;
|
|
LosProcessCB *process = OsCurrProcessGet();
|
|
|
|
if ((process->consoleID > CONSOLE_NUM - 1) || (process->consoleID < 0)) {
|
|
return;
|
|
}
|
|
|
|
consoleId = process->consoleID;
|
|
CONSOLE_CB *consoleCB = g_console[consoleId];
|
|
(VOID)OsKillLock(consoleCB->pgrpId, SIGINT);
|
|
}
|
|
|
|
STATIC INT32 UserFilepRead(CONSOLE_CB *consoleCB, struct file *filep, const struct file_operations_vfs *fops,
|
|
CHAR *buffer, size_t bufLen)
|
|
{
|
|
INT32 ret;
|
|
INT32 needreturn = LOS_NOK;
|
|
CHAR ch;
|
|
INT32 lastTokenType = STAT_NOMAL_KEY;
|
|
|
|
if (fops->read == NULL) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
/* Non-ICANON mode */
|
|
if ((consoleCB->consoleTermios.c_lflag & ICANON) == 0) {
|
|
ret = fops->read(filep, buffer, bufLen);
|
|
if (ret < 0) {
|
|
return -EPERM;
|
|
}
|
|
return ret;
|
|
}
|
|
/* ICANON mode: store data to console buffer, read data and stored data into console fifo */
|
|
if (consoleCB->currentLen == 0) {
|
|
while (1) {
|
|
ret = fops->read(filep, &ch, EACH_CHAR);
|
|
if (ret <= 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (IsNeedContinue(consoleCB, ch, &lastTokenType))
|
|
continue;
|
|
|
|
switch (ch) {
|
|
case '\r':
|
|
ch = '\n';
|
|
case '\n':
|
|
EchoToTerminal(consoleCB, filep, fops, ch);
|
|
UserEndOfRead(consoleCB, filep, fops);
|
|
ret = ConsoleReadFifo(buffer, consoleCB, bufLen);
|
|
|
|
needreturn = LOS_OK;
|
|
break;
|
|
case '\b':
|
|
default:
|
|
EchoToTerminal(consoleCB, filep, fops, ch);
|
|
StoreReadChar(consoleCB, ch, ret);
|
|
break;
|
|
}
|
|
|
|
if (needreturn == LOS_OK)
|
|
break;
|
|
}
|
|
} else {
|
|
/* if data is already in console fifo, we returen them immediately */
|
|
ret = ConsoleReadFifo(buffer, consoleCB, bufLen);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
INT32 FilepRead(struct file *filep, const struct file_operations_vfs *fops, CHAR *buffer, size_t bufLen)
|
|
{
|
|
INT32 ret;
|
|
if (fops->read == NULL) {
|
|
return -EFAULT;
|
|
}
|
|
/*
|
|
* adopt uart read function to read data from filep
|
|
* and write data to buffer (filep is
|
|
* corresponding to filep of /dev/console)
|
|
*/
|
|
ret = fops->read(filep, buffer, bufLen);
|
|
if (ret < 0) {
|
|
return -EPERM;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
INT32 FilepWrite(struct file *filep, const struct file_operations_vfs *fops, const CHAR *buffer, size_t bufLen)
|
|
{
|
|
INT32 ret;
|
|
if (fops->write == NULL) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
ret = fops->write(filep, buffer, bufLen);
|
|
if (ret < 0) {
|
|
return -EPERM;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
INT32 FilepClose(struct file *filep, const struct file_operations_vfs *fops)
|
|
{
|
|
INT32 ret;
|
|
if ((fops == NULL) || (fops->close == NULL)) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
/*
|
|
* adopt uart close function to open filep (filep is
|
|
* corresponding to filep of /dev/console)
|
|
*/
|
|
ret = fops->close(filep);
|
|
if (ret < 0) {
|
|
return -EPERM;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
INT32 FilepIoctl(struct file *filep, const struct file_operations_vfs *fops, INT32 cmd, unsigned long arg)
|
|
{
|
|
INT32 ret;
|
|
if (fops->ioctl == NULL) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
ret = fops->ioctl(filep, cmd, arg);
|
|
if (ret < 0) {
|
|
return -EPERM;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
INT32 FilepPoll(struct file *filep, const struct file_operations_vfs *fops, poll_table *fds)
|
|
{
|
|
INT32 ret;
|
|
if (fops->poll == NULL) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
/*
|
|
* adopt uart poll function to poll filep (filep is
|
|
* corresponding to filep of /dev/serial)
|
|
*/
|
|
ret = fops->poll(filep, fds);
|
|
if (ret < 0) {
|
|
return -EPERM;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
STATIC INT32 ConsoleOpen(struct file *filep)
|
|
{
|
|
INT32 ret;
|
|
UINT32 consoleID;
|
|
struct file *privFilep = NULL;
|
|
const struct file_operations_vfs *fileOps = NULL;
|
|
|
|
consoleID = (UINT32)OsConsoleFullpathToID(filep->f_path);
|
|
if (consoleID == (UINT32)-1) {
|
|
ret = EPERM;
|
|
goto ERROUT;
|
|
}
|
|
filep->f_priv = g_console[consoleID - 1];
|
|
|
|
ret = GetFilepOps(filep, &privFilep, &fileOps);
|
|
if (ret != ENOERR) {
|
|
ret = EINVAL;
|
|
goto ERROUT;
|
|
}
|
|
ret = FilepOpen(privFilep, fileOps);
|
|
if (ret < 0) {
|
|
ret = EPERM;
|
|
goto ERROUT;
|
|
}
|
|
return ENOERR;
|
|
|
|
ERROUT:
|
|
set_errno(ret);
|
|
return VFS_ERROR;
|
|
}
|
|
|
|
STATIC INT32 ConsoleClose(struct file *filep)
|
|
{
|
|
INT32 ret;
|
|
struct file *privFilep = NULL;
|
|
const struct file_operations_vfs *fileOps = NULL;
|
|
|
|
ret = GetFilepOps(filep, &privFilep, &fileOps);
|
|
if (ret != ENOERR) {
|
|
ret = EINVAL;
|
|
goto ERROUT;
|
|
}
|
|
ret = FilepClose(privFilep, fileOps);
|
|
if (ret < 0) {
|
|
ret = EPERM;
|
|
goto ERROUT;
|
|
}
|
|
|
|
return ENOERR;
|
|
|
|
ERROUT:
|
|
set_errno(ret);
|
|
return VFS_ERROR;
|
|
}
|
|
|
|
STATIC ssize_t DoRead(CONSOLE_CB *consoleCB, CHAR *buffer, size_t bufLen,
|
|
struct file *privFilep,
|
|
const struct file_operations_vfs *fileOps)
|
|
{
|
|
INT32 ret;
|
|
|
|
#ifdef LOSCFG_SHELL
|
|
if (OsCurrTaskGet()->taskID == consoleCB->shellEntryId) {
|
|
ret = FilepRead(privFilep, fileOps, buffer, bufLen);
|
|
} else {
|
|
#endif
|
|
(VOID)ConsoleCtrlRightsCapture(consoleCB);
|
|
ret = UserFilepRead(consoleCB, privFilep, fileOps, buffer, bufLen);
|
|
(VOID)ConsoleCtrlRightsRelease(consoleCB);
|
|
#ifdef LOSCFG_SHELL
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
STATIC ssize_t ConsoleRead(struct file *filep, CHAR *buffer, size_t bufLen)
|
|
{
|
|
INT32 ret;
|
|
struct file *privFilep = NULL;
|
|
CONSOLE_CB *consoleCB = NULL;
|
|
CHAR *sbuffer = NULL;
|
|
BOOL userBuf = FALSE;
|
|
const struct file_operations_vfs *fileOps = NULL;
|
|
|
|
if ((buffer == NULL) || (bufLen == 0)) {
|
|
ret = EINVAL;
|
|
goto ERROUT;
|
|
}
|
|
|
|
if (bufLen > CONSOLE_FIFO_SIZE) {
|
|
bufLen = CONSOLE_FIFO_SIZE;
|
|
}
|
|
|
|
userBuf = LOS_IsUserAddressRange((vaddr_t)(UINTPTR)buffer, bufLen);
|
|
ret = GetFilepOps(filep, &privFilep, &fileOps);
|
|
if (ret != ENOERR) {
|
|
ret = -EINVAL;
|
|
goto ERROUT;
|
|
}
|
|
consoleCB = (CONSOLE_CB *)filep->f_priv;
|
|
if (consoleCB == NULL) {
|
|
consoleCB = OsGetConsoleByTaskID(OsCurrTaskGet()->taskID);
|
|
if (consoleCB == NULL) {
|
|
return -EFAULT;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* shell task use FilepRead function to get data,
|
|
* user task use UserFilepRead to get data
|
|
*/
|
|
sbuffer = userBuf ? LOS_MemAlloc(m_aucSysMem0, bufLen) : buffer;
|
|
if (sbuffer == NULL) {
|
|
ret = -ENOMEM;
|
|
goto ERROUT;
|
|
}
|
|
|
|
ret = DoRead(consoleCB, sbuffer, bufLen, privFilep, fileOps);
|
|
if (ret < 0) {
|
|
goto ERROUT;
|
|
}
|
|
|
|
if (userBuf) {
|
|
if (LOS_ArchCopyToUser(buffer, sbuffer, bufLen) != 0) {
|
|
ret = -EFAULT;
|
|
goto ERROUT;
|
|
}
|
|
|
|
LOS_MemFree(m_aucSysMem0, sbuffer);
|
|
}
|
|
|
|
return ret;
|
|
|
|
ERROUT:
|
|
if ((userBuf) && (sbuffer != NULL)) {
|
|
LOS_MemFree(m_aucSysMem0, sbuffer);
|
|
}
|
|
set_errno(-ret);
|
|
return VFS_ERROR;
|
|
}
|
|
|
|
STATIC ssize_t DoWrite(CirBufSendCB *cirBufSendCB, CHAR *buffer, size_t bufLen)
|
|
{
|
|
INT32 cnt;
|
|
size_t writen = 0;
|
|
size_t toWrite = bufLen;
|
|
UINT32 intSave;
|
|
|
|
#ifdef LOSCFG_SHELL_DMESG
|
|
(VOID)OsLogMemcpyRecord(buffer, bufLen);
|
|
if (OsCheckConsoleLock()) {
|
|
return 0;
|
|
}
|
|
#endif
|
|
LOS_CirBufLock(&cirBufSendCB->cirBufCB, &intSave);
|
|
while (writen < (INT32)bufLen) {
|
|
/* Transform for CR/LR mode */
|
|
if ((buffer[writen] == '\n') || (buffer[writen] == '\r')) {
|
|
(VOID)LOS_CirBufWrite(&cirBufSendCB->cirBufCB, "\r", 1);
|
|
}
|
|
|
|
cnt = LOS_CirBufWrite(&cirBufSendCB->cirBufCB, &buffer[writen], 1);
|
|
if (cnt <= 0) {
|
|
break;
|
|
}
|
|
toWrite -= cnt;
|
|
writen += cnt;
|
|
}
|
|
LOS_CirBufUnlock(&cirBufSendCB->cirBufCB, intSave);
|
|
/* Log is cached but not printed when a system exception occurs */
|
|
if (OsGetSystemStatus() == OS_SYSTEM_NORMAL) {
|
|
(VOID)LOS_EventWrite(&cirBufSendCB->sendEvent, CONSOLE_CIRBUF_EVENT);
|
|
}
|
|
|
|
return writen;
|
|
}
|
|
|
|
STATIC ssize_t ConsoleWrite(struct file *filep, const CHAR *buffer, size_t bufLen)
|
|
{
|
|
INT32 ret;
|
|
CHAR *sbuffer = NULL;
|
|
BOOL userBuf = FALSE;
|
|
CirBufSendCB *cirBufSendCB = NULL;
|
|
struct file *privFilep = NULL;
|
|
const struct file_operations_vfs *fileOps = NULL;
|
|
|
|
if ((buffer == NULL) || (bufLen == 0)) {
|
|
ret = EINVAL;
|
|
goto ERROUT;
|
|
}
|
|
|
|
if (bufLen > CONSOLE_FIFO_SIZE) {
|
|
bufLen = CONSOLE_FIFO_SIZE;
|
|
}
|
|
|
|
userBuf = LOS_IsUserAddressRange((vaddr_t)(UINTPTR)buffer, bufLen);
|
|
|
|
ret = GetFilepOps(filep, &privFilep, &fileOps);
|
|
if ((ret != ENOERR) || (fileOps->write == NULL) || (filep->f_priv == NULL)) {
|
|
ret = EINVAL;
|
|
goto ERROUT;
|
|
}
|
|
cirBufSendCB = ((CONSOLE_CB *)filep->f_priv)->cirBufSendCB;
|
|
|
|
/*
|
|
* adopt uart open function to read data from buffer
|
|
* and write data to filep (filep is
|
|
* corresponding to filep of /dev/console)
|
|
*/
|
|
sbuffer = userBuf ? LOS_MemAlloc(m_aucSysMem0, bufLen) : (CHAR *)buffer;
|
|
if (sbuffer == NULL) {
|
|
ret = ENOMEM;
|
|
goto ERROUT;
|
|
}
|
|
|
|
if (userBuf && (LOS_ArchCopyFromUser(sbuffer, buffer, bufLen) != 0)) {
|
|
ret = EFAULT;
|
|
goto ERROUT;
|
|
}
|
|
ret = DoWrite(cirBufSendCB, sbuffer, bufLen);
|
|
|
|
if (userBuf) {
|
|
LOS_MemFree(m_aucSysMem0, sbuffer);
|
|
}
|
|
|
|
return ret;
|
|
|
|
ERROUT:
|
|
if (userBuf && sbuffer != NULL) {
|
|
LOS_MemFree(m_aucSysMem0, sbuffer);
|
|
}
|
|
|
|
set_errno(ret);
|
|
return VFS_ERROR;
|
|
}
|
|
|
|
STATIC INT32 ConsoleSetSW(CONSOLE_CB *consoleCB, unsigned long arg)
|
|
{
|
|
struct termios kerTermios;
|
|
UINT32 intSave;
|
|
|
|
if (LOS_ArchCopyFromUser(&kerTermios, (struct termios *)arg, sizeof(struct termios)) != 0) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
LOS_SpinLockSave(&g_consoleSpin, &intSave);
|
|
(VOID)ConsoleTcSetAttr(consoleCB->fd, 0, &kerTermios);
|
|
LOS_SpinUnlockRestore(&g_consoleSpin, intSave);
|
|
return LOS_OK;
|
|
}
|
|
|
|
#define DEFAULT_WINDOW_SIZE_COL 80
|
|
#define DEFAULT_WINDOW_SIZE_ROW 24
|
|
STATIC INT32 ConsoleGetWinSize(unsigned long arg)
|
|
{
|
|
struct winsize kws = {
|
|
.ws_col = DEFAULT_WINDOW_SIZE_COL,
|
|
.ws_row = DEFAULT_WINDOW_SIZE_ROW
|
|
};
|
|
|
|
if (LOS_CopyFromKernel((VOID *)arg, sizeof(struct winsize), &kws, sizeof(struct winsize)) != 0) {
|
|
return -EFAULT;
|
|
}
|
|
return LOS_OK;
|
|
}
|
|
|
|
STATIC INT32 ConsoleGetTermios(unsigned long arg)
|
|
{
|
|
struct file *filep = NULL;
|
|
CONSOLE_CB *consoleCB = NULL;
|
|
|
|
INT32 ret = fs_getfilep(0, &filep);
|
|
if (ret < 0) {
|
|
return -EPERM;
|
|
}
|
|
|
|
consoleCB = (CONSOLE_CB *)filep->f_priv;
|
|
if (consoleCB == NULL) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (LOS_ArchCopyToUser((VOID *)arg, &consoleCB->consoleTermios, sizeof(struct termios)) != 0) {
|
|
return -EFAULT;
|
|
} else {
|
|
return LOS_OK;
|
|
}
|
|
}
|
|
|
|
INT32 ConsoleSetPgrp(CONSOLE_CB *consoleCB, unsigned long arg)
|
|
{
|
|
if (LOS_ArchCopyFromUser(&consoleCB->pgrpId, (INT32 *)(UINTPTR)arg, sizeof(INT32)) != 0) {
|
|
return -EFAULT;
|
|
}
|
|
return LOS_OK;
|
|
}
|
|
|
|
INT32 ConsoleGetPgrp(CONSOLE_CB *consoleCB, unsigned long arg)
|
|
{
|
|
if (LOS_ArchCopyToUser((VOID *)arg, &consoleCB->pgrpId, sizeof(INT32)) != 0) {
|
|
return -EFAULT;
|
|
}
|
|
return LOS_OK;
|
|
}
|
|
|
|
STATIC INT32 ConsoleIoctl(struct file *filep, INT32 cmd, unsigned long arg)
|
|
{
|
|
INT32 ret;
|
|
struct file *privFilep = NULL;
|
|
CONSOLE_CB *consoleCB = NULL;
|
|
const struct file_operations_vfs *fileOps = NULL;
|
|
|
|
ret = GetFilepOps(filep, &privFilep, &fileOps);
|
|
if (ret != ENOERR) {
|
|
ret = EINVAL;
|
|
goto ERROUT;
|
|
}
|
|
|
|
if (fileOps->ioctl == NULL) {
|
|
ret = EFAULT;
|
|
goto ERROUT;
|
|
}
|
|
|
|
consoleCB = (CONSOLE_CB *)filep->f_priv;
|
|
if (consoleCB == NULL) {
|
|
ret = EINVAL;
|
|
goto ERROUT;
|
|
}
|
|
|
|
switch (cmd) {
|
|
case CONSOLE_CONTROL_RIGHTS_CAPTURE:
|
|
ret = ConsoleCtrlRightsCapture(consoleCB);
|
|
break;
|
|
case CONSOLE_CONTROL_RIGHTS_RELEASE:
|
|
ret = ConsoleCtrlRightsRelease(consoleCB);
|
|
break;
|
|
case CONSOLE_CONTROL_CAPTURE_LINE:
|
|
ret = ConsoleCtrlCaptureLine(consoleCB);
|
|
break;
|
|
case CONSOLE_CONTROL_CAPTURE_CHAR:
|
|
ret = ConsoleCtrlCaptureChar(consoleCB);
|
|
break;
|
|
case CONSOLE_CONTROL_REG_USERTASK:
|
|
ret = ConsoleTaskReg(consoleCB->consoleID, arg);
|
|
break;
|
|
case TIOCGWINSZ:
|
|
ret = ConsoleGetWinSize(arg);
|
|
break;
|
|
case TCSETSW:
|
|
ret = ConsoleSetSW(consoleCB, arg);
|
|
break;
|
|
case TCGETS:
|
|
ret = ConsoleGetTermios(arg);
|
|
break;
|
|
case TIOCGPGRP:
|
|
ret = ConsoleGetPgrp(consoleCB, arg);
|
|
break;
|
|
case TIOCSPGRP:
|
|
ret = ConsoleSetPgrp(consoleCB, arg);
|
|
break;
|
|
default:
|
|
if ((cmd == UART_CFG_ATTR || cmd == UART_CFG_PRIVATE)
|
|
&& !LOS_IsUserAddress(arg)) {
|
|
ret = EINVAL;
|
|
goto ERROUT;
|
|
}
|
|
ret = fileOps->ioctl(privFilep, cmd, arg);
|
|
break;
|
|
}
|
|
|
|
if (ret < 0) {
|
|
ret = EPERM;
|
|
goto ERROUT;
|
|
}
|
|
|
|
return ret;
|
|
ERROUT:
|
|
set_errno(ret);
|
|
return VFS_ERROR;
|
|
}
|
|
|
|
STATIC INT32 ConsolePoll(struct file *filep, poll_table *fds)
|
|
{
|
|
INT32 ret;
|
|
struct file *privFilep = NULL;
|
|
const struct file_operations_vfs *fileOps = NULL;
|
|
|
|
ret = GetFilepOps(filep, &privFilep, &fileOps);
|
|
if (ret != ENOERR) {
|
|
ret = EINVAL;
|
|
goto ERROUT;
|
|
}
|
|
|
|
ret = FilepPoll(privFilep, fileOps, fds);
|
|
if (ret < 0) {
|
|
ret = EPERM;
|
|
goto ERROUT;
|
|
}
|
|
return ret;
|
|
|
|
ERROUT:
|
|
set_errno(ret);
|
|
return VFS_ERROR;
|
|
}
|
|
|
|
/* console device driver function structure */
|
|
STATIC const struct file_operations_vfs g_consoleDevOps = {
|
|
.open = ConsoleOpen, /* open */
|
|
.close = ConsoleClose, /* close */
|
|
.read = ConsoleRead, /* read */
|
|
.write = ConsoleWrite, /* write */
|
|
.seek = NULL,
|
|
.ioctl = ConsoleIoctl,
|
|
.mmap = NULL,
|
|
#ifndef CONFIG_DISABLE_POLL
|
|
.poll = ConsolePoll,
|
|
#endif
|
|
};
|
|
|
|
STATIC VOID OsConsoleTermiosInit(CONSOLE_CB *consoleCB, const CHAR *deviceName)
|
|
{
|
|
struct termios consoleTermios = {0};
|
|
|
|
if ((deviceName != NULL) &&
|
|
(strlen(deviceName) == strlen(SERIAL)) &&
|
|
(!strncmp(deviceName, SERIAL, strlen(SERIAL)))) {
|
|
consoleCB->isNonBlock = SetSerialBlock(consoleCB);
|
|
|
|
/* set console to have a buffer for user */
|
|
(VOID)ConsoleTcGetAttr(consoleCB->fd, &consoleTermios);
|
|
consoleTermios.c_lflag |= ICANON | ECHO;
|
|
consoleTermios.c_cc[VINTR] = 3; /* /003 for ^C */
|
|
(VOID)ConsoleTcSetAttr(consoleCB->fd, 0, &consoleTermios);
|
|
}
|
|
#ifdef LOSCFG_NET_TELNET
|
|
else if ((deviceName != NULL) &&
|
|
(strlen(deviceName) == strlen(TELNET)) &&
|
|
(!strncmp(deviceName, TELNET, strlen(TELNET)))) {
|
|
consoleCB->isNonBlock = SetTelnetBlock(consoleCB);
|
|
/* set console to have a buffer for user */
|
|
(VOID)ConsoleTcGetAttr(consoleCB->fd, &consoleTermios);
|
|
consoleTermios.c_lflag |= ICANON | ECHO;
|
|
consoleTermios.c_cc[VINTR] = 3; /* /003 for ^C */
|
|
(VOID)ConsoleTcSetAttr(consoleCB->fd, 0, &consoleTermios);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
STATIC INT32 OsConsoleFileInit(CONSOLE_CB *consoleCB)
|
|
{
|
|
INT32 ret;
|
|
struct Vnode *vnode = NULL;
|
|
struct file *filep = NULL;
|
|
CHAR *fullpath = NULL;
|
|
|
|
ret = vfs_normalize_path(NULL, consoleCB->name, &fullpath);
|
|
if (ret < 0) {
|
|
return EINVAL;
|
|
}
|
|
|
|
VnodeHold();
|
|
ret = VnodeLookup(fullpath, &vnode, 0);
|
|
if (ret != LOS_OK) {
|
|
ret = EACCES;
|
|
goto ERROUT_WITH_FULLPATH;
|
|
}
|
|
|
|
consoleCB->fd = files_allocate(vnode, O_RDWR, (off_t)0, consoleCB, STDERR_FILENO + 1);
|
|
if (consoleCB->fd < 0) {
|
|
ret = EMFILE;
|
|
goto ERROUT_WITH_FULLPATH;
|
|
}
|
|
|
|
ret = fs_getfilep(consoleCB->fd, &filep);
|
|
if (ret < 0) {
|
|
ret = EPERM;
|
|
goto ERROUT_WITH_FULLPATH;
|
|
}
|
|
filep->f_path = fullpath;
|
|
filep->ops = (struct file_operations_vfs *)((struct drv_data *)vnode->data)->ops;
|
|
VnodeDrop();
|
|
return LOS_OK;
|
|
|
|
ERROUT_WITH_FULLPATH:
|
|
VnodeDrop();
|
|
free(fullpath);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Initialized console control platform so that when we operate /dev/console
|
|
* as if we are operating /dev/ttyS0 (uart0).
|
|
*/
|
|
STATIC INT32 OsConsoleDevInit(CONSOLE_CB *consoleCB, const CHAR *deviceName)
|
|
{
|
|
INT32 ret;
|
|
struct file *filep = NULL;
|
|
struct Vnode *vnode = NULL;
|
|
struct file_operations_vfs *devOps = NULL;
|
|
|
|
/* allocate memory for filep,in order to unchange the value of filep */
|
|
filep = (struct file *)LOS_MemAlloc(m_aucSysMem0, sizeof(struct file));
|
|
if (filep == NULL) {
|
|
ret = ENOMEM;
|
|
goto ERROUT;
|
|
}
|
|
|
|
VnodeHold();
|
|
ret = VnodeLookup(deviceName, &vnode, V_DUMMY);
|
|
VnodeDrop(); // not correct, but can't fix perfectly here
|
|
if (ret != LOS_OK) {
|
|
ret = EACCES;
|
|
PRINTK("!! can not find %s\n", consoleCB->name);
|
|
goto ERROUT;
|
|
}
|
|
|
|
consoleCB->devVnode = vnode;
|
|
|
|
/*
|
|
* initialize the console filep which is associated with /dev/console,
|
|
* assign the uart0 vnode of /dev/ttyS0 to console inod of /dev/console,
|
|
* then we can operate console's filep as if we operate uart0 filep of
|
|
* /dev/ttyS0.
|
|
*/
|
|
(VOID)memset_s(filep, sizeof(struct file), 0, sizeof(struct file));
|
|
filep->f_oflags = O_RDWR;
|
|
filep->f_pos = 0;
|
|
filep->f_vnode = vnode;
|
|
filep->f_path = NULL;
|
|
filep->f_priv = NULL;
|
|
/*
|
|
* Use filep to connect console and uart, we can find uart driver function throught filep.
|
|
* now we can operate /dev/console to operate /dev/ttyS0 through filep.
|
|
*/
|
|
devOps = (struct file_operations_vfs *)((struct drv_data*)vnode->data)->ops;
|
|
if (devOps != NULL && devOps->open != NULL) {
|
|
(VOID)devOps->open(filep);
|
|
} else {
|
|
ret = ENOSYS;
|
|
goto ERROUT;
|
|
}
|
|
|
|
ret = register_driver(consoleCB->name, &g_consoleDevOps, DEFFILEMODE, filep);
|
|
if (ret != LOS_OK) {
|
|
goto ERROUT;
|
|
}
|
|
|
|
return LOS_OK;
|
|
|
|
ERROUT:
|
|
if (filep) {
|
|
(VOID)LOS_MemFree(m_aucSysMem0, filep);
|
|
}
|
|
|
|
set_errno(ret);
|
|
return LOS_NOK;
|
|
}
|
|
|
|
STATIC UINT32 OsConsoleDevDeinit(const CONSOLE_CB *consoleCB)
|
|
{
|
|
return unregister_driver(consoleCB->name);
|
|
}
|
|
|
|
STATIC CirBufSendCB *ConsoleCirBufCreate(VOID)
|
|
{
|
|
UINT32 ret;
|
|
CHAR *fifo = NULL;
|
|
CirBufSendCB *cirBufSendCB = NULL;
|
|
CirBuf *cirBufCB = NULL;
|
|
|
|
cirBufSendCB = (CirBufSendCB *)LOS_MemAlloc(m_aucSysMem0, sizeof(CirBufSendCB));
|
|
if (cirBufSendCB == NULL) {
|
|
return NULL;
|
|
}
|
|
(VOID)memset_s(cirBufSendCB, sizeof(CirBufSendCB), 0, sizeof(CirBufSendCB));
|
|
|
|
fifo = (CHAR *)LOS_MemAlloc(m_aucSysMem0, CONSOLE_CIRCBUF_SIZE);
|
|
if (fifo == NULL) {
|
|
goto ERROR_WITH_SENDCB;
|
|
}
|
|
(VOID)memset_s(fifo, CONSOLE_CIRCBUF_SIZE, 0, CONSOLE_CIRCBUF_SIZE);
|
|
|
|
cirBufCB = &cirBufSendCB->cirBufCB;
|
|
ret = LOS_CirBufInit(cirBufCB, fifo, CONSOLE_CIRCBUF_SIZE);
|
|
if (ret != LOS_OK) {
|
|
goto ERROR_WITH_FIFO;
|
|
}
|
|
|
|
(VOID)LOS_EventInit(&cirBufSendCB->sendEvent);
|
|
return cirBufSendCB;
|
|
|
|
ERROR_WITH_FIFO:
|
|
(VOID)LOS_MemFree(m_aucSysMem0, cirBufCB->fifo);
|
|
ERROR_WITH_SENDCB:
|
|
(VOID)LOS_MemFree(m_aucSysMem0, cirBufSendCB);
|
|
return NULL;
|
|
}
|
|
|
|
STATIC VOID ConsoleCirBufDelete(CirBufSendCB *cirBufSendCB)
|
|
{
|
|
CirBuf *cirBufCB = &cirBufSendCB->cirBufCB;
|
|
|
|
(VOID)LOS_MemFree(m_aucSysMem0, cirBufCB->fifo);
|
|
LOS_CirBufDeinit(cirBufCB);
|
|
(VOID)LOS_EventDestroy(&cirBufSendCB->sendEvent);
|
|
(VOID)LOS_MemFree(m_aucSysMem0, cirBufSendCB);
|
|
}
|
|
|
|
STATIC UINT32 OsConsoleBufInit(CONSOLE_CB *consoleCB)
|
|
{
|
|
UINT32 ret;
|
|
TSK_INIT_PARAM_S initParam = {0};
|
|
|
|
consoleCB->cirBufSendCB = ConsoleCirBufCreate();
|
|
if (consoleCB->cirBufSendCB == NULL) {
|
|
return LOS_NOK;
|
|
}
|
|
|
|
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)ConsoleSendTask;
|
|
initParam.usTaskPrio = SHELL_TASK_PRIORITY;
|
|
initParam.auwArgs[0] = (UINTPTR)consoleCB;
|
|
initParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
|
|
if (consoleCB->consoleID == CONSOLE_SERIAL) {
|
|
initParam.pcName = "SendToSer";
|
|
} else {
|
|
initParam.pcName = "SendToTelnet";
|
|
}
|
|
initParam.uwResved = LOS_TASK_STATUS_DETACHED;
|
|
|
|
ret = LOS_TaskCreate(&consoleCB->sendTaskID, &initParam);
|
|
if (ret != LOS_OK) {
|
|
ConsoleCirBufDelete(consoleCB->cirBufSendCB);
|
|
consoleCB->cirBufSendCB = NULL;
|
|
return LOS_NOK;
|
|
}
|
|
(VOID)LOS_EventRead(&consoleCB->cirBufSendCB->sendEvent, CONSOLE_SEND_TASK_RUNNING,
|
|
LOS_WAITMODE_OR | LOS_WAITMODE_CLR, LOS_WAIT_FOREVER);
|
|
|
|
return LOS_OK;
|
|
}
|
|
|
|
STATIC VOID OsConsoleBufDeinit(CONSOLE_CB *consoleCB)
|
|
{
|
|
CirBufSendCB *cirBufSendCB = consoleCB->cirBufSendCB;
|
|
|
|
consoleCB->cirBufSendCB = NULL;
|
|
(VOID)LOS_EventWrite(&cirBufSendCB->sendEvent, CONSOLE_SEND_TASK_EXIT);
|
|
}
|
|
|
|
STATIC CONSOLE_CB *OsConsoleCBInit(UINT32 consoleID)
|
|
{
|
|
CONSOLE_CB *consoleCB = (CONSOLE_CB *)LOS_MemAlloc((VOID *)m_aucSysMem0, sizeof(CONSOLE_CB));
|
|
if (consoleCB == NULL) {
|
|
return NULL;
|
|
}
|
|
(VOID)memset_s(consoleCB, sizeof(CONSOLE_CB), 0, sizeof(CONSOLE_CB));
|
|
|
|
consoleCB->consoleID = consoleID;
|
|
consoleCB->pgrpId = -1;
|
|
consoleCB->shellEntryId = SHELL_ENTRYID_INVALID; /* initialize shellEntryId to an invalid value */
|
|
consoleCB->name = LOS_MemAlloc((VOID *)m_aucSysMem0, CONSOLE_NAMELEN);
|
|
if (consoleCB->name == NULL) {
|
|
PRINT_ERR("consoleCB->name malloc failed\n");
|
|
(VOID)LOS_MemFree((VOID *)m_aucSysMem0, consoleCB);
|
|
return NULL;
|
|
}
|
|
return consoleCB;
|
|
}
|
|
|
|
STATIC VOID OsConsoleCBDeinit(CONSOLE_CB *consoleCB)
|
|
{
|
|
(VOID)LOS_MemFree((VOID *)m_aucSysMem0, consoleCB->name);
|
|
consoleCB->name = NULL;
|
|
(VOID)LOS_MemFree((VOID *)m_aucSysMem0, consoleCB);
|
|
}
|
|
|
|
STATIC CONSOLE_CB *OsConsoleCreate(UINT32 consoleID, const CHAR *deviceName)
|
|
{
|
|
INT32 ret;
|
|
CONSOLE_CB *consoleCB = OsConsoleCBInit(consoleID);
|
|
if (consoleCB == NULL) {
|
|
PRINT_ERR("console malloc error.\n");
|
|
return NULL;
|
|
}
|
|
|
|
ret = snprintf_s(consoleCB->name, CONSOLE_NAMELEN, CONSOLE_NAMELEN - 1,
|
|
"%s%u", CONSOLE, consoleCB->consoleID);
|
|
if (ret == -1) {
|
|
PRINT_ERR("consoleCB->name snprintf_s failed\n");
|
|
goto ERR_WITH_NAME;
|
|
}
|
|
|
|
ret = (INT32)OsConsoleBufInit(consoleCB);
|
|
if (ret != LOS_OK) {
|
|
PRINT_ERR("console OsConsoleBufInit error. %d\n", ret);
|
|
goto ERR_WITH_NAME;
|
|
}
|
|
|
|
ret = (INT32)LOS_SemCreate(1, &consoleCB->consoleSem);
|
|
if (ret != LOS_OK) {
|
|
PRINT_ERR("creat sem for uart failed\n");
|
|
goto ERR_WITH_BUF;
|
|
}
|
|
|
|
ret = OsConsoleDevInit(consoleCB, deviceName);
|
|
if (ret != LOS_OK) {
|
|
PRINT_ERR("console OsConsoleDevInit error. %d\n", ret);
|
|
goto ERR_WITH_SEM;
|
|
}
|
|
|
|
ret = OsConsoleFileInit(consoleCB);
|
|
if (ret != LOS_OK) {
|
|
PRINT_ERR("console OsConsoleFileInit error. %d\n", ret);
|
|
goto ERR_WITH_DEV;
|
|
}
|
|
|
|
OsConsoleTermiosInit(consoleCB, deviceName);
|
|
return consoleCB;
|
|
|
|
ERR_WITH_DEV:
|
|
ret = (INT32)OsConsoleDevDeinit(consoleCB);
|
|
if (ret != LOS_OK) {
|
|
PRINT_ERR("OsConsoleDevDeinit failed!\n");
|
|
}
|
|
ERR_WITH_SEM:
|
|
(VOID)LOS_SemDelete(consoleCB->consoleSem);
|
|
ERR_WITH_BUF:
|
|
OsConsoleBufDeinit(consoleCB);
|
|
ERR_WITH_NAME:
|
|
OsConsoleCBDeinit(consoleCB);
|
|
return NULL;
|
|
}
|
|
|
|
STATIC UINT32 OsConsoleDelete(CONSOLE_CB *consoleCB)
|
|
{
|
|
UINT32 ret;
|
|
|
|
(VOID)files_close(consoleCB->fd);
|
|
ret = OsConsoleDevDeinit(consoleCB);
|
|
if (ret != LOS_OK) {
|
|
PRINT_ERR("OsConsoleDevDeinit failed!\n");
|
|
}
|
|
OsConsoleBufDeinit((CONSOLE_CB *)consoleCB);
|
|
(VOID)LOS_SemDelete(consoleCB->consoleSem);
|
|
(VOID)LOS_MemFree(m_aucSysMem0, consoleCB->name);
|
|
consoleCB->name = NULL;
|
|
(VOID)LOS_MemFree(m_aucSysMem0, consoleCB);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Initialized system console and return stdinfd stdoutfd stderrfd */
|
|
INT32 system_console_init(const CHAR *deviceName)
|
|
{
|
|
#ifdef LOSCFG_SHELL
|
|
UINT32 ret;
|
|
#endif
|
|
INT32 consoleID;
|
|
UINT32 intSave;
|
|
CONSOLE_CB *consoleCB = NULL;
|
|
|
|
consoleID = OsGetConsoleID(deviceName);
|
|
if (consoleID == -1) {
|
|
PRINT_ERR("device is full.\n");
|
|
return VFS_ERROR;
|
|
}
|
|
|
|
consoleCB = OsConsoleCreate((UINT32)consoleID, deviceName);
|
|
if (consoleCB == NULL) {
|
|
PRINT_ERR("%s, %d\n", __FUNCTION__, __LINE__);
|
|
return VFS_ERROR;
|
|
}
|
|
|
|
LOS_SpinLockSave(&g_consoleSpin, &intSave);
|
|
g_console[consoleID - 1] = consoleCB;
|
|
if (OsCurrTaskGet() != NULL) {
|
|
g_taskConsoleIDArray[OsCurrTaskGet()->taskID] = (UINT8)consoleID;
|
|
}
|
|
LOS_SpinUnlockRestore(&g_consoleSpin, intSave);
|
|
|
|
#ifdef LOSCFG_SHELL
|
|
ret = OsShellInit(consoleID);
|
|
if (ret != LOS_OK) {
|
|
PRINT_ERR("%s, %d\n", __FUNCTION__, __LINE__);
|
|
LOS_SpinLockSave(&g_consoleSpin, &intSave);
|
|
(VOID)OsConsoleDelete(consoleCB);
|
|
g_console[consoleID - 1] = NULL;
|
|
g_taskConsoleIDArray[OsCurrTaskGet()->taskID] = 0;
|
|
LOS_SpinUnlockRestore(&g_consoleSpin, intSave);
|
|
return VFS_ERROR;
|
|
}
|
|
#endif
|
|
|
|
return ENOERR;
|
|
}
|
|
|
|
INT32 system_console_deinit(const CHAR *deviceName)
|
|
{
|
|
UINT32 ret;
|
|
CONSOLE_CB *consoleCB = NULL;
|
|
UINT32 taskIdx;
|
|
LosTaskCB *taskCB = NULL;
|
|
UINT32 intSave;
|
|
|
|
consoleCB = OsGetConsoleByDevice(deviceName);
|
|
if (consoleCB == NULL) {
|
|
return VFS_ERROR;
|
|
}
|
|
|
|
#ifdef LOSCFG_SHELL
|
|
(VOID)OsShellDeinit((INT32)consoleCB->consoleID);
|
|
#endif
|
|
|
|
LOS_SpinLockSave(&g_consoleSpin, &intSave);
|
|
/* Redirect all tasks to serial as telnet was unavailable after deinitializing */
|
|
for (taskIdx = 0; taskIdx < g_taskMaxNum; taskIdx++) {
|
|
taskCB = ((LosTaskCB *)g_taskCBArray) + taskIdx;
|
|
if (OsTaskIsUnused(taskCB)) {
|
|
continue;
|
|
} else {
|
|
g_taskConsoleIDArray[taskCB->taskID] = CONSOLE_SERIAL;
|
|
}
|
|
}
|
|
g_console[consoleCB->consoleID - 1] = NULL;
|
|
LOS_SpinUnlockRestore(&g_consoleSpin, intSave);
|
|
|
|
ret = OsConsoleDelete(consoleCB);
|
|
if (ret != LOS_OK) {
|
|
PRINT_ERR("%s, Failed to system_console_deinit\n", __FUNCTION__);
|
|
return VFS_ERROR;
|
|
}
|
|
|
|
return ENOERR;
|
|
}
|
|
|
|
BOOL ConsoleEnable(VOID)
|
|
{
|
|
INT32 consoleID;
|
|
|
|
if (OsCurrTaskGet() != NULL) {
|
|
consoleID = g_taskConsoleIDArray[OsCurrTaskGet()->taskID];
|
|
if (g_uart_fputc_en == 0) {
|
|
if ((g_console[CONSOLE_TELNET - 1] != NULL) && OsPreemptable()) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (consoleID == 0) {
|
|
return FALSE;
|
|
} else if ((consoleID == CONSOLE_TELNET) || (consoleID == CONSOLE_SERIAL)) {
|
|
if ((OsGetSystemStatus() == OS_SYSTEM_NORMAL) && !OsPreemptable()) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
#if defined (LOSCFG_DRIVERS_USB_SERIAL_GADGET) || defined (LOSCFG_DRIVERS_USB_ETH_SER_GADGET)
|
|
else if ((SerialTypeGet() == SERIAL_TYPE_USBTTY_DEV) && (userial_mask_get() == 1)) {
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
INT32 ConsoleTaskReg(INT32 consoleID, UINT32 taskID)
|
|
{
|
|
if (g_console[consoleID - 1]->shellEntryId == SHELL_ENTRYID_INVALID) {
|
|
g_console[consoleID - 1]->shellEntryId = taskID;
|
|
(VOID)OsSetCurrProcessGroupID(OsGetUserInitProcessID());
|
|
return LOS_OK;
|
|
}
|
|
|
|
return LOS_NOK;
|
|
}
|
|
|
|
BOOL SetSerialNonBlock(const CONSOLE_CB *consoleCB)
|
|
{
|
|
INT32 ret;
|
|
|
|
if (consoleCB == NULL) {
|
|
PRINT_ERR("%s: Input parameter is illegal\n", __FUNCTION__);
|
|
return FALSE;
|
|
}
|
|
ret = ioctl(consoleCB->fd, CONSOLE_CMD_RD_BLOCK_SERIAL, CONSOLE_RD_NONBLOCK);
|
|
if (ret != 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SetSerialBlock(const CONSOLE_CB *consoleCB)
|
|
{
|
|
INT32 ret;
|
|
|
|
if (consoleCB == NULL) {
|
|
PRINT_ERR("%s: Input parameter is illegal\n", __FUNCTION__);
|
|
return TRUE;
|
|
}
|
|
ret = ioctl(consoleCB->fd, CONSOLE_CMD_RD_BLOCK_SERIAL, CONSOLE_RD_BLOCK);
|
|
if (ret != 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL SetTelnetNonBlock(const CONSOLE_CB *consoleCB)
|
|
{
|
|
INT32 ret;
|
|
|
|
if (consoleCB == NULL) {
|
|
PRINT_ERR("%s: Input parameter is illegal\n", __FUNCTION__);
|
|
return FALSE;
|
|
}
|
|
ret = ioctl(consoleCB->fd, CONSOLE_CMD_RD_BLOCK_TELNET, CONSOLE_RD_NONBLOCK);
|
|
if (ret != 0) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SetTelnetBlock(const CONSOLE_CB *consoleCB)
|
|
{
|
|
INT32 ret;
|
|
|
|
if (consoleCB == NULL) {
|
|
PRINT_ERR("%s: Input parameter is illegal\n", __FUNCTION__);
|
|
return TRUE;
|
|
}
|
|
ret = ioctl(consoleCB->fd, CONSOLE_CMD_RD_BLOCK_TELNET, CONSOLE_RD_BLOCK);
|
|
if (ret != 0) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL is_nonblock(const CONSOLE_CB *consoleCB)
|
|
{
|
|
if (consoleCB == NULL) {
|
|
PRINT_ERR("%s: Input parameter is illegal\n", __FUNCTION__);
|
|
return FALSE;
|
|
}
|
|
return consoleCB->isNonBlock;
|
|
}
|
|
|
|
INT32 ConsoleUpdateFd(VOID)
|
|
{
|
|
INT32 consoleID;
|
|
|
|
if (OsCurrTaskGet() != NULL) {
|
|
consoleID = g_taskConsoleIDArray[(OsCurrTaskGet())->taskID];
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
if (g_uart_fputc_en == 0) {
|
|
if (g_console[CONSOLE_TELNET - 1] != NULL) {
|
|
consoleID = CONSOLE_TELNET;
|
|
} else {
|
|
return -1;
|
|
}
|
|
} else if (consoleID == 0) {
|
|
if (g_console[CONSOLE_SERIAL - 1] != NULL) {
|
|
consoleID = CONSOLE_SERIAL;
|
|
} else if (g_console[CONSOLE_TELNET - 1] != NULL) {
|
|
consoleID = CONSOLE_TELNET;
|
|
} else {
|
|
PRINTK("No console dev used.\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (g_console[consoleID - 1] == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
return g_console[consoleID - 1]->fd;
|
|
}
|
|
|
|
CONSOLE_CB *OsGetConsoleByID(INT32 consoleID)
|
|
{
|
|
if (consoleID != CONSOLE_TELNET) {
|
|
consoleID = CONSOLE_SERIAL;
|
|
}
|
|
return g_console[consoleID - 1];
|
|
}
|
|
|
|
CONSOLE_CB *OsGetConsoleByTaskID(UINT32 taskID)
|
|
{
|
|
INT32 consoleID = g_taskConsoleIDArray[taskID];
|
|
|
|
return OsGetConsoleByID(consoleID);
|
|
}
|
|
|
|
VOID OsSetConsoleID(UINT32 newTaskID, UINT32 curTaskID)
|
|
{
|
|
if ((newTaskID >= LOSCFG_BASE_CORE_TSK_LIMIT) || (curTaskID >= LOSCFG_BASE_CORE_TSK_LIMIT)) {
|
|
return;
|
|
}
|
|
|
|
g_taskConsoleIDArray[newTaskID] = g_taskConsoleIDArray[curTaskID];
|
|
}
|
|
|
|
STATIC ssize_t WriteToTerminal(const CONSOLE_CB *consoleCB, const CHAR *buffer, size_t bufLen)
|
|
{
|
|
INT32 ret, fd;
|
|
INT32 cnt = 0;
|
|
struct file *privFilep = NULL;
|
|
struct file *filep = NULL;
|
|
const struct file_operations_vfs *fileOps = NULL;
|
|
|
|
fd = consoleCB->fd;
|
|
ret = fs_getfilep(fd, &filep);
|
|
ret = GetFilepOps(filep, &privFilep, &fileOps);
|
|
|
|
if ((fileOps == NULL) || (fileOps->write == NULL)) {
|
|
ret = EFAULT;
|
|
goto ERROUT;
|
|
}
|
|
(VOID)fileOps->write(privFilep, buffer, bufLen);
|
|
|
|
return cnt;
|
|
|
|
ERROUT:
|
|
set_errno(ret);
|
|
return VFS_ERROR;
|
|
}
|
|
|
|
STATIC UINT32 ConsoleSendTask(UINTPTR param)
|
|
{
|
|
CONSOLE_CB *consoleCB = (CONSOLE_CB *)param;
|
|
CirBufSendCB *cirBufSendCB = consoleCB->cirBufSendCB;
|
|
CirBuf *cirBufCB = &cirBufSendCB->cirBufCB;
|
|
UINT32 ret, size;
|
|
UINT32 intSave;
|
|
CHAR *buf = NULL;
|
|
|
|
(VOID)LOS_EventWrite(&cirBufSendCB->sendEvent, CONSOLE_SEND_TASK_RUNNING);
|
|
|
|
while (1) {
|
|
ret = LOS_EventRead(&cirBufSendCB->sendEvent, CONSOLE_CIRBUF_EVENT | CONSOLE_SEND_TASK_EXIT,
|
|
LOS_WAITMODE_OR | LOS_WAITMODE_CLR, LOS_WAIT_FOREVER);
|
|
if (ret == CONSOLE_CIRBUF_EVENT) {
|
|
size = LOS_CirBufUsedSize(cirBufCB);
|
|
if (size == 0) {
|
|
continue;
|
|
}
|
|
buf = (CHAR *)LOS_MemAlloc(m_aucSysMem1, size + 1);
|
|
if (buf == NULL) {
|
|
continue;
|
|
}
|
|
(VOID)memset_s(buf, size + 1, 0, size + 1);
|
|
|
|
LOS_CirBufLock(cirBufCB, &intSave);
|
|
(VOID)LOS_CirBufRead(cirBufCB, buf, size);
|
|
LOS_CirBufUnlock(cirBufCB, intSave);
|
|
|
|
(VOID)WriteToTerminal(consoleCB, buf, size);
|
|
(VOID)LOS_MemFree(m_aucSysMem1, buf);
|
|
} else if (ret == CONSOLE_SEND_TASK_EXIT) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ConsoleCirBufDelete(cirBufSendCB);
|
|
return LOS_OK;
|
|
}
|
|
|
|
#ifdef LOSCFG_KERNEL_SMP
|
|
VOID OsWaitConsoleSendTaskPend(UINT32 taskID)
|
|
{
|
|
UINT32 i;
|
|
CONSOLE_CB *console = NULL;
|
|
LosTaskCB *taskCB = NULL;
|
|
INT32 waitTime = 3000; /* 3000: 3 seconds */
|
|
|
|
for (i = 0; i < CONSOLE_NUM; i++) {
|
|
console = g_console[i];
|
|
if (console == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (OS_TID_CHECK_INVALID(console->sendTaskID)) {
|
|
continue;
|
|
}
|
|
|
|
taskCB = OS_TCB_FROM_TID(console->sendTaskID);
|
|
while ((waitTime > 0) && (taskCB->taskEvent == NULL) && (taskID != console->sendTaskID)) {
|
|
LOS_Mdelay(1); /* 1: wait console task pend */
|
|
--waitTime;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID OsWakeConsoleSendTask(VOID)
|
|
{
|
|
UINT32 i;
|
|
CONSOLE_CB *console = NULL;
|
|
|
|
for (i = 0; i < CONSOLE_NUM; i++) {
|
|
console = g_console[i];
|
|
if (console == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (console->cirBufSendCB != NULL) {
|
|
(VOID)LOS_EventWrite(&console->cirBufSendCB->sendEvent, CONSOLE_CIRBUF_EVENT);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|