使用NAPI的完整例子

本文详细解析了HNS NIC驱动的初始化过程、队列管理、中断处理机制及NAPI的使用。介绍了如何通过hns_nic_init_ring_data进行队列数据结构的初始化,并通过hns_nic_ring_open开启队列,最后深入探讨了中断处理流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

hns_nic_try_get_ae->hns_nic_init_ring_data
static int hns_nic_init_ring_data(struct hns_nic_priv *priv)
{
struct hnae_handle *h = priv->ae_handle;
struct hns_nic_ring_data *rd;
bool is_ver1 = AE_IS_VER1(priv->enet_ver);
int i;


if (h->q_num > NIC_MAX_Q_PER_VF) {
netdev_err(priv->netdev, "too much queue (%d)\n", h->q_num);
return -EINVAL;
}


priv->ring_data = kzalloc(h->q_num * sizeof(*priv->ring_data) * 2,
 GFP_KERNEL);
if (!priv->ring_data)
return -ENOMEM;


for (i = 0; i < h->q_num; i++) {
rd = &priv->ring_data[i];
rd->queue_index = i;
rd->ring = &h->qs[i]->tx_ring;
rd->poll_one = hns_nic_tx_poll_one;
rd->fini_process = is_ver1 ? hns_nic_tx_fini_pro :
hns_nic_tx_fini_pro_v2;
//调用netif_napi_add将priv->netdev和NAPI的结构rd->napi绑定,注意这里的poll函数是hns_nic_common_poll
netif_napi_add(priv->netdev, &rd->napi,
      hns_nic_common_poll, NIC_TX_CLEAN_MAX_NUM);
rd->ring->irq_init_flag = RCB_IRQ_NOT_INITED;
}
for (i = h->q_num; i < h->q_num * 2; i++) {
rd = &priv->ring_data[i];
rd->queue_index = i - h->q_num;
rd->ring = &h->qs[i - h->q_num]->rx_ring;
rd->poll_one = hns_nic_rx_poll_one;
rd->ex_process = hns_nic_rx_up_pro;
rd->fini_process = is_ver1 ? hns_nic_rx_fini_pro :
hns_nic_rx_fini_pro_v2;


netif_napi_add(priv->netdev, &rd->napi,
      hns_nic_common_poll, NIC_RX_CLEAN_MAX_NUM);
rd->ring->irq_init_flag = RCB_IRQ_NOT_INITED;
}


return 0;
}


hns_nic_net_open->hns_nic_net_up->hns_nic_ring_open
static int hns_nic_ring_open(struct net_device *netdev, int idx)
{
struct hns_nic_priv *priv = netdev_priv(netdev);
struct hnae_handle *h = priv->ae_handle;


napi_enable(&priv->ring_data[idx].napi);


enable_irq(priv->ring_data[idx].ring->irq);
h->dev->ops->toggle_ring_irq(priv->ring_data[idx].ring, 0);


return 0;
}
在hns_nic_ring_open 中调用napi_enable来使能napi




这样当hns的中断到来是,会调用中断的处理函数
hns_nic_net_open->hns_nic_net_up->hns_nic_init_irq
static int hns_nic_init_irq(struct hns_nic_priv *priv)
{
struct hnae_handle *h = priv->ae_handle;
struct hns_nic_ring_data *rd;
int i;
int ret;
int cpu;


for (i = 0; i < h->q_num * 2; i++) {
rd = &priv->ring_data[i];


if (rd->ring->irq_init_flag == RCB_IRQ_INITED)
break;


snprintf(rd->ring->ring_name, RCB_RING_NAME_LEN,
"%s-%s%d", priv->netdev->name,
(is_tx_ring(rd->ring) ? "tx" : "rx"), rd->queue_index);


rd->ring->ring_name[RCB_RING_NAME_LEN - 1] = '\0';
//rx和tx的中断处理函数都是hns_irq_handle
ret = request_irq(rd->ring->irq,
 hns_irq_handle, 0, rd->ring->ring_name, rd);
if (ret) {
netdev_err(priv->netdev, "request irq(%d) fail\n",
  rd->ring->irq);
return ret;
}


return 0;
}


static irqreturn_t hns_irq_handle(int irq, void *dev)
{
struct hns_nic_ring_data *ring_data = (struct hns_nic_ring_data *)dev;


ring_data->ring->q->handle->dev->ops->toggle_ring_irq(
ring_data->ring, 1);
napi_schedule(&ring_data->napi);


return IRQ_HANDLED;
}
在中断处理函数hns_irq_handle 中会调用napi_schedule 来调度NAPI
napi_schedule->__napi_schedule
void __napi_schedule(struct napi_struct *n)
{
unsigned long flags;


local_irq_save(flags);
____napi_schedule(this_cpu_ptr(&softnet_data), n);
local_irq_restore(flags);
}
static inline void ____napi_schedule(struct softnet_data *sd,
    struct napi_struct *napi)
{
list_add_tail(&napi->poll_list, &sd->poll_list);
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
}


最终在____napi_schedule 通过__raise_softirq_irqoff来触发一个软件中断,这个软件中断的回调函数
是在net_dev_init 中注册的.


static int __init net_dev_init(void)
{
for_each_possible_cpu(i) {
struct work_struct *flush = per_cpu_ptr(&flush_works, i);
struct softnet_data *sd = &per_cpu(softnet_data, i);


INIT_WORK(flush, flush_backlog);


skb_queue_head_init(&sd->input_pkt_queue);
skb_queue_head_init(&sd->process_queue);
INIT_LIST_HEAD(&sd->poll_list);
sd->output_queue_tailp = &sd->output_queue;
#ifdef CONFIG_RPS
sd->csd.func = rps_trigger_softirq;
sd->csd.info = sd;
sd->cpu = i;
#endif


sd->backlog.poll = process_backlog;
sd->backlog.weight = weight_p;
}
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);


}
net_rx_action->napi_poll
static int napi_poll(struct napi_struct *n, struct list_head *repoll)
{
if (test_bit(NAPI_STATE_SCHED, &n->state)) {
work = n->poll(n, weight);
trace_napi_poll(n, work, weight);
}


WARN_ON_ONCE(work > weight);


if (likely(work < weight))
goto out_unlock;


/* Drivers must not modify the NAPI state if they
* consume the entire weight.  In such cases this code
* still "owns" the NAPI instance and therefore can
* move the instance around on the list at-will.
*/
if (unlikely(napi_disable_pending(n))) {
napi_complete(n);
goto out_unlock;
}
}
最终还是设备自己的poll函数,如果处理完所有的包后调用napi_complete
从前面可知设备自己注册的poll函数为hns_nic_common_poll
static int hns_nic_common_poll(struct napi_struct *napi, int budget)
{
int clean_complete = 0;
struct hns_nic_ring_data *ring_data =
container_of(napi, struct hns_nic_ring_data, napi);
struct hnae_ring *ring = ring_data->ring;


try_again:
//再针对tx和rx分别调用自己的poll_one 函数
clean_complete += ring_data->poll_one(
ring_data, budget - clean_complete,
ring_data->ex_process);


if (clean_complete < budget) {
//如果处理完成调用napi_complete 告诉NAPI 已经处理完成了
if (ring_data->fini_process(ring_data)) {
napi_complete(napi);
ring->q->handle->dev->ops->toggle_ring_irq(ring, 0);
} else {
goto try_again;
}
}


return clean_complete;
}


我们继续看hns_nic_rx_poll_one中会通过下面的code调用
/* do update ip stack process*/
((void (*)(struct hns_nic_ring_data *, struct sk_buff *))v)(
ring_data, skb);
这里的v就是hns_nic_rx_up_pro
static void hns_nic_rx_up_pro(struct hns_nic_ring_data *ring_data,
     struct sk_buff *skb)
{
struct net_device *ndev = ring_data->napi.dev;


skb->protocol = eth_type_trans(skb, ndev);
(void)napi_gro_receive(&ring_data->napi, skb);
}
 最终会在hns_nic_rx_up_pro 中调用napi_gro_receive 将在hns_nic_rx_poll_one 中准备好的skb发送给上层.
<think>好的,我需要帮助用户了解如何使用napi_value操作USBDevice,特别是在Node.js的N-API环境中。首先,我得确认用户的问题是关于在Node.js的N-API中使用napi_value结构来处理USB设备操作的示例。用户可能希望看到一个具体的代码示例,以及相关的步骤解释。 首先,我应该回忆一下N-API的基本概念。N-API是Node.js提供的一个API层,用于构建原生插件,它独立于底层的JavaScript运行时(如V8),提高了跨版本兼容性。napi_value是N-API中用来表示JavaScript值的数据类型,所有JavaScript值在C/C++中都是通过napi_value来传递的。 接下来,用户提到的USBDevice可能是指访问USB设备的接口。在Node.js中,通常可以通过第三方库(如usb)来操作USB设备,但用户希望使用N-API直接进行操作。这意味着需要编写原生插件来调用系统级的USB相关函数。 首先,我需要确认如何通过N-API创建一个JavaScript对象来表示USB设备。这涉及到使用napi_create_object函数。然后,可能需要为这个对象添加属性和方法,例如设备名称、厂商ID、产品ID等属性,以及打开、关闭设备等方法。 接下来,需要考虑如何从系统获取USB设备的信息。在Linux系统中,可能需要通过libusb库或者直接与内核交互来枚举USB设备。这里可能需要调用系统函数或者使用现有的库,但要注意在原生插件中如何集成这些库。 然后,将获取到的USB设备信息转换为napi_value类型,并封装成JavaScript对象返回给Node.js环境。这可能涉及到字符串转换、数值转换,以及设置对象属性。 另外,用户可能需要示例代码,所以需要编写一个简单的例子,展示如何用N-API创建一个USBDevice对象,并添加一些基本的方法,比如打开和关闭设备。同时,需要处理错误和异常,使用N-API提供的错误处理函数,如napi_throw_error。 还需要注意引用用户提供的参考资料,尤其是引用[2]提到的of_find_matching_node_and_match函数,这可能与在Linux系统中查找设备节点有关,但不确定是否直接相关。可能需要进一步确认,但暂时可以忽略,除非用户明确提到设备树相关的内容。 另外,用户提到的引用[3]关于鸿蒙系统的资料,可能与当前问题无关,但需要确保示例代码适用于用户的环境,比如Linux系统。因此,在示例中应该注明系统依赖,比如需要libusb库。 在步骤分解中,应该包括初始化N-API模块、定义导出函数、创建USBDevice对象、设置属性和方法,以及底层的系统调用。同时,需要说明如何编译原生模块,并提醒用户安装必要的依赖项。 可能还需要考虑异步操作的问题,比如USB设备的读写通常是异步的,但在N-API中处理异步回调可能比较复杂。如果用户只是需要一个基本示例,可能暂时不需要涉及异步处理,保持示例简单。 最后,生成的相关问题应该围绕N-API的其他应用、USB操作的深入问题,以及错误处理等方面,帮助用户扩展知识。</think>在Node.js N-API中使用`napi_value`操作USB设备需要结合系统级USB接口和N-API对象封装。以下是实现步骤及示例: --- ### 1. **N-API模块初始化** 在原生模块中初始化USB设备操作接口: ```c #include <node_api.h> #include <libusb-1.0/libusb.h> // 依赖libusb库 // 定义USBDevice对象包装器 typedef struct { libusb_device_handle* handle; int vendor_id; int product_id; } USBDevice; ``` --- ### 2. **封装USBDevice对象** 通过`napi_define_class`创建JavaScript类: ```c napi_value CreateUSBDevice(napi_env env, napi_callback_info info) { napi_value result; USBDevice* usb_device; // 创建napi_value对象并绑定USBDevice结构 napi_create_object(env, &result); napi_wrap(env, result, usb_device, NULL, NULL, NULL); return result; } ``` --- ### 3. **添加设备操作方法** 示例:封装`open`方法: ```c napi_value OpenDevice(napi_env env, napi_callback_info info) { napi_value this_arg; USBDevice* usb_device; // 从napi_value中提取原生对象 napi_get_cb_info(env, info, NULL, NULL, &this_arg, NULL); napi_unwrap(env, this_arg, (void**)&usb_device); // 调用libusb打开设备 int ret = libusb_open(usb_device->handle, &usb_device->handle); if (ret != LIBUSB_SUCCESS) { napi_throw_error(env, NULL, "Failed to open USB device"); } return NULL; } ``` --- ### 4. **导出模块接口** 注册模块方法供JavaScript调用: ```c napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor desc[] = { {"open", NULL, OpenDevice, NULL, NULL, NULL, napi_default, NULL} }; // 定义USBDevice类并导出 napi_define_class(env, "USBDevice", NAPI_AUTO_LENGTH, CreateUSBDevice, NULL, 1, desc, &exports); return exports; } NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) ``` --- ### 5. **JavaScript调用示例** ```javascript const addon = require('./build/Release/usb_addon.node'); const device = new addon.USBDevice(); device.open(); // 调用原生方法 ``` --- ### 关键点说明 1. **依赖管理**:需安装`libusb`库(Linux: `sudo apt-get install libusb-1.0-0-dev`) 2. **线程安全**:USB操作需通过异步Worker避免阻塞事件循环[^3] 3. **错误处理**:使用`napi_throw_error`抛出异常到JavaScript层 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值