串行驱动程序 startup/request 函数未被执行
serial driver startup/request functions aren't being executed
我正在为 UART(Xilinx 的 AXI UART Lite)编写我的第一个串行驱动程序。我知道内核中有一个,但我不是在嵌入式 Linux 环境中工作。
使用the documentation和现有代码作为参考,我已经实现了我的驱动程序。但是,我无法验证正在调用我的 startup
或 request_port
函数。
启动函数基本上做what this does. However, not using an IRQ I'm polling. Likewise, the request_port function I have is quite similar to this function.
文档指出 the startup function 在端口打开时被调用一次。因为我在 /dev 中确实有我期望的设备节点,所以我想我需要打开端口。在 minicom 的步骤中,我打开文件(使用 lsof 验证)。然而,我可以在 dmesg 中找到 none 的消息,这些消息是我放置在我的 startup
或 request_port
函数中的。
很抱歉延迟回复,但我不得不在发布代码之前跳过一些障碍。这是我的:
#define MAX_SUPPORTED_UARTS 1
#define DRIVER_MAJOR_NUM 243 /* local/experimental */
#define DRIVER_MINOR_NUM 0
static int request_port(struct uart_port *);
static void release_port(struct uart_port *);
static int startup(struct uart_port *);
static void shutdown(struct uart_port *);
static struct uart_ops axilite_ops = {
.release_port = release_port,
.request_port = request_port,
.startup = startup,
.shutdown = shutdown,
};
static struct uart_port axilite_ports[MAX_SUPPORTED_UARTS] = {
{
.mapbase = 0, /* determined during discovery */
.membase = 0, /* calcuated after learning mapbase */
.iotype = UPIO_MEM, /* from serial_core.c */
.irq = 0, /* currently, device doesn't use an IRQ, just polling */
.fifosize = 16, /* directly Xilinx documentation */
.ops = &axilite_ops,
.flags = UPF_FIXED_TYPE | UPF_FIXED_PORT | UPF_SPD_VHI,
.mctrl = TIOCM_CTS | TIOCM_DSR | TIOCM_CAR, /* from uartlite.c in kernel tree */
},
};
static struct uart_driver axilite_driver = {
.owner = THIS_MODULE,
.driver_name = "axilite",
.dev_name = "ttyAUL",
.major = DRIVER_MAJOR_NUM,
.minor = DRIVER_MINOR_NUM,
.nr = MAX_SUPPORTED_UARTS,
};
static int startup(struct uart_port *pup)
{
void __iomem* pmem = pup->membase;
uint32_t ctlreg = 0;
pr_info("%s:%d entry point\n", __func__, __LINE__);
ctlreg = ioread32(CTL_REG_ADDR(pmem));
pr_info("DEBUG: %s:%d CTL Reg is 0x%08x\n",
__func__, __LINE__, ctlreg);
/* clear the FIFO's */
ctlreg |= CTL_REG_RST_TX | CTL_REG_RST_RX;
iowrite32(ctlreg, CTL_REG_ADDR(pmem));
return 0;
}
static void shutdown(struct uart_port *pup)
{
void __iomem* pmem = pup->membase;
pr_info("%s:%d entry point\n", __func__, __LINE__);
/* Using a queue from the axilite driver in the kernel */
iowrite32(0, CTL_REG_ADDR(pmem));
}
static void release_port(struct uart_port *pup)
{
pr_info("%s:%d entry point\n", __func__, __LINE__);
if (pup->membase)
iounmap(pup->membase);
pup->membase = NULL;
}
static int request_port(struct uart_port *pup)
{
int result = 0;
uint32_t bus_addr = pup->mapbase;
void __iomem* pmem = NULL;
pr_info("%s:%d mapping bus addr to virtual space\n",
__func__, __LINE__);
pmem = ioremap_nocache(bus_addr, UART_REGISTER_REGION);
if (IS_ERR_OR_NULL(pmem)) {
pr_err("%s:%d ioremap_nocache failed\n",
__func__, __LINE__);
result = PTR_ERR(pmem);
goto startup_exit;
}
pup->membase = pmem;
pr_info("%s:%d busaddr 0x%08x mapped to %p\n",
__func__, __LINE__, bus_addr, pmem);
startup_exit:
return result;
}
static int add_uart_ports(void)
{
int result = 0;
uint32_t i = 0, baseaddr = 0;
pr_info("%s:%d adding %d port(s) to the system\n",
__func__, __LINE__, uart_count);
/* calls into code which learns the base address,
* verified that it works
*/
baseaddr = get_uart_base(i);
if ((int)baseaddr == -EINVAL) {
pr_err("%s:%d Invalid index used\n", __func__, __LINE__);
result = (int)baseaddr;
goto add_ports_exit;
}
else if (0 == baseaddr) {
pr_err("%s:%d Invalid bus address encountered, port add stopped\n",
__func__, __LINE__);
result = -ENODEV;
goto add_ports_exit;
}
else
axilite_port[i].mapbase = baseaddr;
result = uart_add_one_port(&axilite_driver, &axilite_port[i]);
if (result) {
pr_err("%s:%d uart_add_one_port failed for port %d\n",
__func__, __LINE__, i);
goto add_ports_exit;
}
/* TODO think of a better way to track whether the port
* is registered. this will work for now.
*/
axilite_port[i].private_data = &uart_count;
init_stage |= UART_PORT_REGISTERED;
add_ports_exit:
return result;
}
static int add_uart_driver(void)
{
int result = 0;
pr_info("%s:%d adding the driver\n", __func__, __LINE__);
uart_count = get_uart_count();
axilite_driver.nr = (int)uart_count;
result = uart_register_driver(&axilite_driver);
if (result) {
pr_err("%s:%d uart_register_driver failed\n",
__func__, __LINE__);
goto add_driver_exit;
}
init_stage |= DRIVER_REGISTERED;
add_driver_exit:
return result;
}
编辑
我没有添加平台驱动程序。虽然这是Xilinx的系统,但是我的驱动不会是嵌入式的运行Linux。鉴于此,我认为驱动程序启动类似于 the driver for 21285。这有误吗?
编辑结束
这可能是什么原因造成的?我应该做什么或检查什么?
安迪
你们提供平台数据吗?驱动程序本身是不够的,你必须向内核提供你的硬件存在的信息,内存映射到特定的基地址,连接到特定的中断,等等......在现代 ARM 系统上你这样做通过设备树,其他系统有不同的手段。只有在提供这些数据后,驱动程序才会加载并启动。
在serial_core.c uart_port_startup
方法中,开头有代码:
if (uport->type == PORT_UNKNOWN)
return 1;
这就是没有调用启动方法的原因。您需要将 struct uart_port.type
设置为一个非未知值。至少它对我有用。您可能需要添加新的 PORT_ 类型或使用现有类型 - 不确定冲突。
对于我的虚拟设备,这有效:
static struct uart_port tiny_port = {
.ops = &tiny_ops,
.type = PORT_8250,
};
我正在为 UART(Xilinx 的 AXI UART Lite)编写我的第一个串行驱动程序。我知道内核中有一个,但我不是在嵌入式 Linux 环境中工作。
使用the documentation和现有代码作为参考,我已经实现了我的驱动程序。但是,我无法验证正在调用我的 startup
或 request_port
函数。
启动函数基本上做what this does. However, not using an IRQ I'm polling. Likewise, the request_port function I have is quite similar to this function.
文档指出 the startup function 在端口打开时被调用一次。因为我在 /dev 中确实有我期望的设备节点,所以我想我需要打开端口。在 minicom 的步骤中,我打开文件(使用 lsof 验证)。然而,我可以在 dmesg 中找到 none 的消息,这些消息是我放置在我的 startup
或 request_port
函数中的。
很抱歉延迟回复,但我不得不在发布代码之前跳过一些障碍。这是我的:
#define MAX_SUPPORTED_UARTS 1
#define DRIVER_MAJOR_NUM 243 /* local/experimental */
#define DRIVER_MINOR_NUM 0
static int request_port(struct uart_port *);
static void release_port(struct uart_port *);
static int startup(struct uart_port *);
static void shutdown(struct uart_port *);
static struct uart_ops axilite_ops = {
.release_port = release_port,
.request_port = request_port,
.startup = startup,
.shutdown = shutdown,
};
static struct uart_port axilite_ports[MAX_SUPPORTED_UARTS] = {
{
.mapbase = 0, /* determined during discovery */
.membase = 0, /* calcuated after learning mapbase */
.iotype = UPIO_MEM, /* from serial_core.c */
.irq = 0, /* currently, device doesn't use an IRQ, just polling */
.fifosize = 16, /* directly Xilinx documentation */
.ops = &axilite_ops,
.flags = UPF_FIXED_TYPE | UPF_FIXED_PORT | UPF_SPD_VHI,
.mctrl = TIOCM_CTS | TIOCM_DSR | TIOCM_CAR, /* from uartlite.c in kernel tree */
},
};
static struct uart_driver axilite_driver = {
.owner = THIS_MODULE,
.driver_name = "axilite",
.dev_name = "ttyAUL",
.major = DRIVER_MAJOR_NUM,
.minor = DRIVER_MINOR_NUM,
.nr = MAX_SUPPORTED_UARTS,
};
static int startup(struct uart_port *pup)
{
void __iomem* pmem = pup->membase;
uint32_t ctlreg = 0;
pr_info("%s:%d entry point\n", __func__, __LINE__);
ctlreg = ioread32(CTL_REG_ADDR(pmem));
pr_info("DEBUG: %s:%d CTL Reg is 0x%08x\n",
__func__, __LINE__, ctlreg);
/* clear the FIFO's */
ctlreg |= CTL_REG_RST_TX | CTL_REG_RST_RX;
iowrite32(ctlreg, CTL_REG_ADDR(pmem));
return 0;
}
static void shutdown(struct uart_port *pup)
{
void __iomem* pmem = pup->membase;
pr_info("%s:%d entry point\n", __func__, __LINE__);
/* Using a queue from the axilite driver in the kernel */
iowrite32(0, CTL_REG_ADDR(pmem));
}
static void release_port(struct uart_port *pup)
{
pr_info("%s:%d entry point\n", __func__, __LINE__);
if (pup->membase)
iounmap(pup->membase);
pup->membase = NULL;
}
static int request_port(struct uart_port *pup)
{
int result = 0;
uint32_t bus_addr = pup->mapbase;
void __iomem* pmem = NULL;
pr_info("%s:%d mapping bus addr to virtual space\n",
__func__, __LINE__);
pmem = ioremap_nocache(bus_addr, UART_REGISTER_REGION);
if (IS_ERR_OR_NULL(pmem)) {
pr_err("%s:%d ioremap_nocache failed\n",
__func__, __LINE__);
result = PTR_ERR(pmem);
goto startup_exit;
}
pup->membase = pmem;
pr_info("%s:%d busaddr 0x%08x mapped to %p\n",
__func__, __LINE__, bus_addr, pmem);
startup_exit:
return result;
}
static int add_uart_ports(void)
{
int result = 0;
uint32_t i = 0, baseaddr = 0;
pr_info("%s:%d adding %d port(s) to the system\n",
__func__, __LINE__, uart_count);
/* calls into code which learns the base address,
* verified that it works
*/
baseaddr = get_uart_base(i);
if ((int)baseaddr == -EINVAL) {
pr_err("%s:%d Invalid index used\n", __func__, __LINE__);
result = (int)baseaddr;
goto add_ports_exit;
}
else if (0 == baseaddr) {
pr_err("%s:%d Invalid bus address encountered, port add stopped\n",
__func__, __LINE__);
result = -ENODEV;
goto add_ports_exit;
}
else
axilite_port[i].mapbase = baseaddr;
result = uart_add_one_port(&axilite_driver, &axilite_port[i]);
if (result) {
pr_err("%s:%d uart_add_one_port failed for port %d\n",
__func__, __LINE__, i);
goto add_ports_exit;
}
/* TODO think of a better way to track whether the port
* is registered. this will work for now.
*/
axilite_port[i].private_data = &uart_count;
init_stage |= UART_PORT_REGISTERED;
add_ports_exit:
return result;
}
static int add_uart_driver(void)
{
int result = 0;
pr_info("%s:%d adding the driver\n", __func__, __LINE__);
uart_count = get_uart_count();
axilite_driver.nr = (int)uart_count;
result = uart_register_driver(&axilite_driver);
if (result) {
pr_err("%s:%d uart_register_driver failed\n",
__func__, __LINE__);
goto add_driver_exit;
}
init_stage |= DRIVER_REGISTERED;
add_driver_exit:
return result;
}
编辑
我没有添加平台驱动程序。虽然这是Xilinx的系统,但是我的驱动不会是嵌入式的运行Linux。鉴于此,我认为驱动程序启动类似于 the driver for 21285。这有误吗?
编辑结束
这可能是什么原因造成的?我应该做什么或检查什么?
安迪
你们提供平台数据吗?驱动程序本身是不够的,你必须向内核提供你的硬件存在的信息,内存映射到特定的基地址,连接到特定的中断,等等......在现代 ARM 系统上你这样做通过设备树,其他系统有不同的手段。只有在提供这些数据后,驱动程序才会加载并启动。
在serial_core.c uart_port_startup
方法中,开头有代码:
if (uport->type == PORT_UNKNOWN)
return 1;
这就是没有调用启动方法的原因。您需要将 struct uart_port.type
设置为一个非未知值。至少它对我有用。您可能需要添加新的 PORT_ 类型或使用现有类型 - 不确定冲突。
对于我的虚拟设备,这有效:
static struct uart_port tiny_port = {
.ops = &tiny_ops,
.type = PORT_8250,
};