两个相同VID PID的不同设备加载同一个KO文件中的不同驱动
问题:Realtek两个不同芯片的USB-TO-ETHERNET,PID VID都同为:8152。因为客户手中已经有一部分老的产品了,目前停产,新的产品使用不同的芯片。
需求:USB-TO-ETHERNET只是我们的外围设备的一部分,所以要求不管客户使用哪一个设备接进来,都能和主机搭配工作。
解决方案概述:
1,这是USB设备,所以通用USB接口标准会读取其设备描述。找出两种设备的关键不同点,我们的情况是:新旧设备的bNumConfigurations值不同,一个是1,另一个是2。
2,将驱动名统一为8152,跟据bNumConfigurations不同,加载不同的函数即可;
过程和代码:
1,先将新设备的驱动加入到kernel中,并确认新的驱动配新的设备可以工作。老的设备就不用说了,因为已经在工作中。
2,PIDVID加载时,只认一个驱动的名字。我这里设为“rtl8152”,所以驱动入口也只有一个。
static struct usb_driver rtl8152_driver = {
.name= MODULENAME,
.probe= rtl8152_probe,
.disconnect= rtl8152_disconnect,
.id_table= rtl8152_table,
.suspend= rtl8152_suspend,
.resume= rtl8152_resume
};
static int __init usb_rtl8152_init(void)
{
returnusb_register(&rtl8152_driver);
}
static void __exit usb_rtl8152_exit(void)
{
usb_deregister(&rtl8152_driver);
}
接下来要区分不同的设备执行不同的函数了。
贴代码最实际:
static int rtl8152_probe(structusb_interface *intf,
const struct usb_device_id *id)
{
structusb_device *udev = interface_to_usbdev(intf);
rtl8152_t*tp;
structnet_device *netdev;
usb_driver_set_configuration(udev,1);
return-ENODEV;
}
if(udev->descriptor.bNumConfigurations==1)
rtl815x=1;//--------------当bNumConfigurations为1时执行老驱动的函数;
else
rtl815x=2;//--------------当bNumConfigurations为其它时执行新驱动的函数;
netdev= alloc_etherdev(sizeof(rtl8152_t));
if(!netdev) {
printk("Outof memory");
return-ENOMEM;
}
tp= netdev_priv(netdev);
tp->msg_enable= 0x7FFF;
tp->intr_buff= kmalloc(INTBUFSIZE, GFP_KERNEL);
if(!tp->intr_buff) {
free_netdev(netdev);
return-ENOMEM;
}
if(rtl815x==1)//老驱动
{
tasklet_init(&tp->tl,r8152u_rx_fixup, (unsigned long)tp);
spin_lock_init(&tp->rx_pool_lock);
INIT_DELAYED_WORK(&tp->schedule,r8152u_rtl_work_func_t);
tp->udev= udev;
tp->netdev= netdev;
#if LINUX_VERSION_CODE >=KERNEL_VERSION(2,6,29)
netdev->netdev_ops= &rtl8152_netdev_ops;
#else
netdev->open= rtl8152u_open;
netdev->stop= rtl8152u_close;
netdev->get_stats= rtl8152u_get_stats;
netdev->hard_start_xmit= rtl8152u_start_xmit;
netdev->tx_timeout= rtl8152u_tx_timeout;
// netdev->change_mtu = eth_change_mtu;
netdev->set_mac_address= rtl8152u_set_mac_address;
netdev->do_ioctl= rtl8152u_ioctl;
netdev->set_multicast_list= rtl8152u_set_rx_mode;
#endif /* HAVE_NET_DEVICE_OPS */
netdev->watchdog_timeo= RTL8152_TX_TIMEOUT;
netdev->features&= ~NETIF_F_IP_CSUM;
SET_ETHTOOL_OPS(netdev,&r8152u_ops);
tp->intr_interval= 100; /* 100ms */
if(!r8152u_alloc_all_urbs(tp)) {
err("outof memory");
gotoout;
}
if(!rtl8152u_reset(tp)) {
err("couldn'treset the device");
gotoout1;
}
r8152u_fill_skb_pool(tp);
rtl8152u_set_ethernet_addr(tp);
}
else
{//新驱动
tasklet_init(&tp->tl,rx_fixup, (unsigned long)tp);
INIT_DELAYED_WORK(&tp->schedule,rtl_work_func_t);
tp->udev= udev;
tp->netdev= netdev;
#if LINUX_VERSION_CODE >=KERNEL_VERSION(2,6,29)
netdev->netdev_ops= &rtl8152_netdev_ops;
#else
netdev->open= rtl8152_open;
netdev->stop= rtl8152_close;
netdev->get_stats= rtl8152_get_stats;
netdev->hard_start_xmit= rtl8152_start_xmit;
netdev->tx_timeout= rtl8152_tx_timeout;
// netdev->change_mtu = eth_change_mtu;
netdev->set_mac_address= rtl8152_set_mac_address;
netdev->do_ioctl= rtl8152_ioctl;
netdev->set_multicast_list= rtl8152_set_rx_mode;
#endif /* HAVE_NET_DEVICE_OPS */
netdev->watchdog_timeo= RTL8152_TX_TIMEOUT;
netdev->features&= ~NETIF_F_IP_CSUM;
SET_ETHTOOL_OPS(netdev,&ops);
tp->intr_interval= 100; /* 100ms */
tp->speed= 0;
r8152b_init(tp);
if(!alloc_all_urbs(tp)) {
printk("outof memory");
gotoout;
}
tp->rx_skb= netdev_alloc_skb_ip_align(netdev, RTL8152_MTU + sizeof(RxDesc));
if(!tp->rx_skb)
gotoout1;
}
usb_set_intfdata(intf,tp);
SET_NETDEV_DEV(netdev,&intf->dev);
if(register_netdev(netdev) != 0) {
printk("couldn'tregister the device");
gotoout2;
}
dev_info(&intf->dev,"%s: rtl8152 is detected\n", netdev->name);
return0;
out2:
usb_set_intfdata(intf,NULL);
if(rtl815x==1)
{
r8152u_free_skb_pool(tp);
}
else
{
dev_kfree_skb(tp->rx_skb);
}
out1:
if(rtl815x==1)
{
r8152u_free_all_urbs(tp);
}
else
{
free_all_urbs(tp);
}
out:
kfree(tp->intr_buff);
free_netdev(netdev);
return-EIO;
}
static void rtl8152_disconnect(structusb_interface *intf)
{
rtl8152_t*tp = usb_get_intfdata(intf);
usb_set_intfdata(intf,NULL);
if(tp) {
set_bit(RTL8152_UNPLUG,&tp->flags);
tasklet_kill(&tp->tl);
unregister_netdev(tp->netdev);
if(rtl815x==1)
{
r8152u_unlink_all_urbs(tp);
r8152u_free_all_urbs(tp);
r8152u_free_skb_pool(tp);
}
else
{
free_all_urbs(tp);
}
if(tp->rx_skb)
dev_kfree_skb(tp->rx_skb);
kfree(tp->intr_buff);
free_netdev(tp->netdev);
}
}
/* table of devices that work with thisdriver */
static struct usb_device_id rtl8152_table[]= {//这个就一样了,就是因为这个PIDVID相同才到//这地步了
{USB_DEVICE_VER(VENDOR_ID_REALTEK,PRODUCT_ID_RTL8152, 0x2000, 0x2000)},
// {USB_DEVICE(VENDOR_ID_REALTEK,PRODUCT_ID_RTL8152)},
{}
};
MODULE_DEVICE_TABLE(usb, rtl8152_table);
其它函数和变量同样可以用全局变量:rtl815x==1/2来区分。再贴代码就没什么意义了。
因为只有一个设备名,和入口函数,系统注册加载时就当一个驱动来加载。只是按不同设备的参数执行不同函数罢了。