USB网卡驱动分析(rt8152)
USB⽹卡驱动分析(rt8152)
USB⽹卡驱动分析(rt8152)
USB设备驱动程序分析
最近⼀直在搞zynq的PL部分,为了保持对驱动程序的敏感度,看着源码分析⼀下rt8152的驱动程序。之前学单⽚机⼀直想着给单⽚机装⼀个USB⽹卡,但是⼀直没有思路。今天突然想到之前的想法,就带着这个想法加上对内核驱动的怀念,写⼀下,写点东西有时候能让⼈的⼼平静点。
USB总线和USB设备
usb总线是⼀种常见的⾼速总线,其实也不算⾼,usb2.0规定没记错应该是480Mbit/s。物理层上,usb high使⽤的是差分电压型串⾏数据线。usb full采⽤的是差分电流型串⾏串⾏数据线。差分抗⼲扰源于模拟电路的⼀个差动放⼤器,放⼤差模信号,抑制共模信号。再来说⼀下为什么⾼速数据总线喜欢⽤电流来传输信号。我们知道当导线距离增加时,其电阻会增加,那么从发送端到接收端的电压就会发⽣变化。使⽤电流传输,在发送端放置⼀个电流源,⽆论电阻怎么变换,流过电阻的电流总是恒定的。只要在接收端的端接电阻保持稳点就可以了。
简单介绍完物理层以后,在说⼀下USB通信的过程。很重要的⼀点是每次USB通信的发起者都是主机。
这点很重要,⾄于USB的中断传输模式、块传输。。。⽹上好多,这⾥只写⼀些实⽤的、简单的。
在linux设备驱动中,把usb控制器的驱动程序可以看成是usb总线驱动程序,在设备树中定义了usb控制器的设备节点,控制器通过和设备树匹配,获取到usb控制器的基地址、中断号等资源。这部分程序⼀般不需要驱动⼯程师去修改,⼀般芯⽚⼚商会写好。⼀般情况下,需要关⼼的是USB设备驱动程序,也就是挂在USB总线上的设备。通俗的讲就是u盘,usb声卡,usb暖⼿宝(这算吗☺)。
USB设备匹配⽅式
usb设备的匹配不需要在设备树中进⾏定义,⽽是通过vid和pid。这是usb控制器在在完成热插拔以后做的⼀项检测⼯作,会从usb的端点0中读取usb的vid和pid,然后在和已经装在的usb驱动程序进⾏匹配。
static struct usb_device_id rtl8152_table[] = {
{REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8152)},
{REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8153)},
{REALTEK_USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101)},
{REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x7205)},
{REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x304f)},
{REALTEK_USB_DEVICE(VENDOR_ID_NVIDIA,  0x09ff)},
{}
};
这是usb⽹卡的设备ID匹配表。当usb设备接到usb总线上,如果正确匹配,probe函数就会被执⾏。
思考
1. usb设备驱动的PID和VID能不能动态配置?
2. usb设备驱动程序中的VID和PID万⼀重复怎么办?
⽹络设备驱动程序分析
⽹卡设备驱动分析
之前写过⼀篇 ⼤致说了下⽹卡驱动程序的结构以及怎么实现⼀个⽹络驱动程序。
这⾥在赘述⼀下,⽹卡驱动程序在probe函数中:
1. struct net_device *netdev;定义⽹络设备结构体;
2. netdev = alloc_etherdev(sizeof(struct r8152));分配空间
3. 填充该结构体;
4. ret = register_netdev(netdev);注册该设备;
5. 实现传输函数以及中断接收⽹络数据。(也可以是轮询,取决于并发量,⽬前有⼀种⾃动检测的驱动程序,第⼀次是中断形式,之后
是轮询);
USB+⽹卡驱动
标题其实已经说明了,USB⽹卡驱动程序就是USB驱动程序加上⽹卡驱动程序。说说题外话吧,如何⽤带usb控制器的单⽚机来和usb⽹卡进⾏数据交互呢?之前做过STM32上的USB⾃定义设备程序,是
从机的(上位机⽤的是libusb+qt)。所以对单⽚机的usb程序有⼀些记忆,基本都是对端点的操作。接下来去分析linux中的usb⽹卡驱动程序,以此来构想下单⽚机如何驱动⼀个usb⽹卡。
linux 源码中rt8152驱动程序分析
源码在drivers\net\usb\r8152.c下
先看下⼊⼝函数:
static int rtl8152_probe(struct usb_interface *intf,const struct usb_device_id *id)上海人民广播电台
{
struct usb_device *udev = interface_to_usbdev(intf);
word字体放大
struct r8152 *tp;
struct net_device *netdev;
int ret;
usb_driver_set_configuration(udev, 1);
usb_reset_device(udev);
netdev = alloc_etherdev(sizeof(struct r8152));
tp->mii.dev = netdev;
tp->mii.mdio_read = read_mii_word;
tp->mii.mdio_write = write_mii_word;
tp->mii.phy_id_mask = 0x3f;
tp-&_num_mask = 0x1f;
tp->mii.phy_id = R8152_PHY_ID;
ret = register_netdev(netdev);
}
把结构性的代码留下了,可以看出在probe函数中,完成了usb操作接⼝的获取、⽹络设备的注册、pyh相关设置。
static int rtl_ops_init(struct r8152 *tp)
{
struct rtl_ops *ops = &tp->rtl_ops;
int ret = 0;
switch (tp->version) {
case RTL_VER_01:
case RTL_VER_02:
ops->init  = r8152b_init;
ops->enable  = rtl8152_enable;
ops->disable  = rtl8152_disable;
ops->up  = rtl8152_up;
ops->down  = rtl8152_down;
ops->unload  = rtl8152_unload;
ops->eee_get  = r8152_get_eee;
ops->eee_set  = r8152_set_eee;
ops->in_nway  = rtl8152_in_nway;
ops->hw_phy_cfg  = r8152b_hw_phy_cfg;
ops->autosuspend_en = rtl_runtime_suspend_enable;
break;
case RTL_VER_03:
case RTL_VER_04:
case RTL_VER_05:
case RTL_VER_06:
ops->init  = r8153_init;
ops->enable  = rtl8153_enable;
ops->disable  = rtl8153_disable;
ops->up  = rtl8153_up;
ops->down  = rtl8153_down;
ops->unload  = rtl8153_unload;
ops->eee_get  = r8153_get_eee;
ops->eee_set  = r8153_set_eee;
ops->in_nway  = rtl8153_in_nway;
ops->hw_phy_cfg  = r8153_hw_phy_cfg;
ops->autosuspend_en = rtl8153_runtime_enable;
break;
default:
ret = -ENODEV;
netif_err(tp, probe, tp->netdev, "Unknown Device\n");
break;
}
return ret;
}
这段代码完成了所有⽹卡的操作函数的注册。
static const struct net_device_ops rtl8152_netdev_ops = {
.
ndo_open  = rtl8152_open,
.ndo_stop  = rtl8152_close,
.ndo_do_ioctl  = rtl8152_ioctl,
.ndo_start_xmit  = rtl8152_start_xmit,
.ndo_tx_timeout  = rtl8152_tx_timeout,
.ndo_set_features = rtl8152_set_features,
.ndo_set_rx_mode = rtl8152_set_rx_mode,
.ndo_set_mac_address = rtl8152_set_mac_address,
.ndo_change_mtu  = rtl8152_change_mtu,
法律硕士考试科目
.ndo_validate_addr = eth_validate_addr,
.ndo_features_check = rtl8152_features_check,
};
rtl8152_start_xmit()函数很重要,完成了⽹络数据包的发送。实现如下:
static netdev_tx_t rtl8152_start_xmit(struct sk_buff *skb,          struct net_device *netdev)
{
struct r8152 *tp = netdev_priv(netdev);
skb_tx_timestamp(skb);
skb_queue_tail(&tp->tx_queue, skb);
if (!list_empty(&tp->tx_free)) {
if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
set_bit(SCHEDULE_NAPI, &tp->flags);
schedule_delayed_work(&tp->schedule, 0);
} else {
usb_mark_last_busy(tp->udev);
napi_schedule(&tp->napi);
五月你好 图片}
} else if (skb_queue_len(&tp->tx_queue) > tp->tx_qlen) {  netif_stop_queue(netdev);
}
return NETDEV_TX_OK;
}
再到usb中断服务函数中到接收⼤代码:
static void tx_bottom(struct r8152 *tp)
{
int res;
do {
struct tx_agg *agg;
if (skb_queue_empty(&tp->tx_queue))
镜双城每个人物结局
break;
agg = r8152_get_tx_agg(tp);
if (!agg)
break;
res = r8152_tx_agg_fill(tp, agg);
if (res) {
struct net_device *netdev = tp->netdev;
if (res == -ENODEV) {工业洗地机哪个品牌好
set_bit(RTL8152_UNPLUG, &tp->flags);
netif_device_detach(netdev);
} else {
struct net_device_stats *stats = &netdev->stats;
unsigned long flags;
netif_warn(tp, tx_err, netdev,
"failed tx_urb %d\n", res);
stats->tx_dropped += agg->skb_num;
spin_lock_irqsave(&tp->tx_lock, flags);
list_add_tail(&agg->list, &tp->tx_free);
spin_unlock_irqrestore(&tp->tx_lock, flags);
}
}
} while (res == 0);
}
这⾥使⽤了中断的下半部,说⼀下这个知识,在linux内核中,中断处理⼀般分为以下⼏种⽅式:
1. 直接处理;
2. 软中断
3. tasklet;
4. 中断线程化;
5. ⼯作队列;
直接处理就是在中断服务函数中直接处理相应的逻辑,这种情况对应于⼗分简短的中断处理逻辑;
软中断和tasklet都是发⽣在中断上下⽂的,因此在处理函数中不能有休眠。⼯作队列和中断线程化可以⽤休眠,但是处理的实时性会差⼀些。
⼤体结构已经出来了,还有⼀个urb的知识没有说;在linux驱动中,通过驱动程序通过urb和设备进⾏通信。urb相当于⽹络设备驱动程序中的skb。是传输数据的载体。
static void write_bulk_callback(struct urb *urb)
{
struct net_device_stats *stats;
struct net_device *netdev;
struct tx_agg *agg;
struct r8152 *tp;
int status = urb->status;
agg = urb->context;
if (!agg)
return;
tp = agg->context;
if (!tp)
return;
netdev = tp->netdev;
stats = &netdev->stats;
if (status) {
if (net_ratelimit())
netdev_warn(netdev, "Tx status %d\n", status);
stats->tx_errors += agg->skb_num;
} else {
stats->tx_packets += agg->skb_num;
stats->tx_bytes += agg->skb_len;
}
spin_lock(&tp->tx_lock);
list_add_tail(&agg->list, &tp->tx_free);
spin_unlock(&tp->tx_lock);
usb_autopm_put_interface_async(tp->intf);
if (!netif_carrier_ok(netdev))
return;
if (!test_bit(WORK_ENABLE, &tp->flags))
return;
if (test_bit(RTL8152_UNPLUG, &tp->flags))
return;
if (!skb_queue_empty(&tp->tx_queue))
napi_schedule(&tp->napi);
}
static void intr_callback(struct urb *urb)
{

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。