本文主要探讨210的block驱动和net驱动。
block
随机存取设备且读写是按块进行,缓冲区用于暂存数据,达条件后一次性写入设备或读到缓冲区
块设备与字符设备:同一设备支持块和字符访问策略,块设备驱动层支持缓冲区,字符设备驱动层没有缓冲
块设备单位:扇区(Sectors):1扇区为512(倍)字节,块(Blocks):1块包含1个或多个扇区,段(Segments):若干相邻块组成,页(Page):内核内存映射管理基本单位
VFS是linux系统内核软件层,统一数据结构管理各种逻辑文件系统,接受用户层对文件系统各种操作,给应用层提供标准文件操作接口,
内核空间层:通用块层(generic Block Layer):负责维持I/O请求在文件系统与底层物理磁盘关系(bio结构对应I/O请求)IO调度层:多请求到块设备,对读写请求排序在执行以扩大效率,映射层(Mapping Layer):将文件访问映射为设备访问
块设备驱动:设备I/O操作,向块设备发出请求(request结构体描述),请求速度很慢,内核提供队列机制把I/O请求添加到队列中,提交请求前内核会先执行请求合并和排序以提高访问的效率,I/O调度程序子系统提交I/O请求,挂起块I/O 请求,决定队列请求顺序和派发请求到设备
结构体
block_device:块设备实例
struct block_device {dev_t bd_dev; /* not a kdev_t - it's a search key */struct inode * bd_inode; /* will die */struct super_block * bd_super;int bd_openers;struct mutex bd_mutex; /* open/close mutex */struct list_head bd_inodes;void * bd_claiming;void * bd_holder;int bd_holders;
#ifdef CONFIG_SYSFSstruct list_head bd_holder_list;
#endifstruct block_device * bd_contains;unsigned bd_block_size;struct hd_struct * bd_part;/* number of times partitions within this device have been opened. */unsigned bd_part_count;int bd_invalidated;struct gendisk * bd_disk;struct list_head bd_list;/** Private data. You must have bd_claim'ed the block_device* to use this. NOTE: bd_claim allows an owner to claim* the same device multiple times, the owner must take special* care to not mess up bd_private for that case.*/unsigned long bd_private;/* The counter of freeze processes */int bd_fsfreeze_count;/* Mutex for freeze */struct mutex bd_fsfreeze_mutex;
};
hd_struct:分区信息
struct hd_struct {sector_t start_sect;sector_t nr_sects;sector_t alignment_offset;unsigned int discard_alignment;struct device __dev;struct kobject *holder_dir;int policy, partno;
#ifdef CONFIG_FAIL_MAKE_REQUESTint make_it_fail;
#endifunsigned long stamp;int in_flight[2];
#ifdef CONFIG_SMPstruct disk_stats __percpu *dkstats;
#elsestruct disk_stats dkstats;
#endifstruct rcu_head rcu_head;char partition_name[GENHD_PART_NAME_SIZE];
};
request:内核请求队列
/** try to put the fields that are referenced together in the same cacheline.* if you modify this structure, be sure to check block/blk-core.c:rq_init()* as well!*/
struct request {struct list_head queuelist;struct call_single_data csd;struct request_queue *q;unsigned int cmd_flags;enum rq_cmd_type_bits cmd_type;unsigned long atomic_flags;int cpu;/* the following two fields are internal, NEVER access directly */unsigned int __data_len; /* total data len */sector_t __sector; /* sector cursor */struct bio *bio;struct bio *biotail;struct hlist_node hash; /* merge hash *//** The rb_node is only used inside the io scheduler, requests* are pruned when moved to the dispatch queue. So let the* completion_data share space with the rb_node.*/union {struct rb_node rb_node; /* sort/lookup */void *completion_data;};/** Three pointers are available for the IO schedulers, if they need* more they have to dynamically allocate it.*/void *elevator_private;void *elevator_private2;void *elevator_private3;struct gendisk *rq_disk;unsigned long start_time;
#ifdef CONFIG_BLK_CGROUPunsigned long long start_time_ns;unsigned long long io_start_time_ns; /* when passed to hardware */
#endif/* Number of scatter-gather DMA addr+len pairs after* physical address coalescing is performed.*/unsigned short nr_phys_segments;unsigned short ioprio;int ref_count;void *special; /* opaque pointer available for LLD use */char *buffer; /* kaddr of the current segment if available */int tag;int errors;/** when request is used as a packet command carrier*/unsigned char __cmd[BLK_MAX_CDB];unsigned char *cmd;unsigned short cmd_len;unsigned int extra_len; /* length of alignment and padding */unsigned int sense_len;unsigned int resid_len; /* residual count */void *sense;unsigned long deadline;struct list_head timeout_list;unsigned int timeout;int retries;/** completion callback.*/rq_end_io_fn *end_io;void *end_io_data;/* for bidi */struct request *next_rq;
};
request_queue:内核申请request资源建立请求链表并填写BIO形成队列
struct request_queue
{/** Together with queue_head for cacheline sharing*/struct list_head queue_head;struct request *last_merge;struct elevator_queue *elevator;/** the queue request freelist, one for reads and one for writes*/struct request_list rq;request_fn_proc *request_fn;make_request_fn *make_request_fn;prep_rq_fn *prep_rq_fn;unplug_fn *unplug_fn;merge_bvec_fn *merge_bvec_fn;prepare_flush_fn *prepare_flush_fn;softirq_done_fn *softirq_done_fn;rq_timed_out_fn *rq_timed_out_fn;dma_drain_needed_fn *dma_drain_needed;lld_busy_fn *lld_busy_fn;/** Dispatch queue sorting*/sector_t end_sector;struct request *boundary_rq;/** Auto-unplugging state*/struct timer_list unplug_timer;int unplug_thresh; /* After this many requests */unsigned long unplug_delay; /* After this many jiffies */struct work_struct unplug_work;struct backing_dev_info backing_dev_info;/** The queue owner gets to use this for whatever they like.* ll_rw_blk doesn't touch it.*/void *queuedata;/** queue needs bounce pages for pages above this limit*/gfp_t bounce_gfp;/** various queue flags, see QUEUE_* below*/unsigned long queue_flags;/** protects queue structures from reentrancy. ->__queue_lock should* _never_ be used directly, it is queue private. always use* ->queue_lock.*/spinlock_t __queue_lock;spinlock_t *queue_lock;/** queue kobject*/struct kobject kobj;/** queue settings*/unsigned long nr_requests; /* Max # of requests */unsigned int nr_congestion_on;unsigned int nr_congestion_off;unsigned int nr_batching;void *dma_drain_buffer;unsigned int dma_drain_size;unsigned int dma_pad_mask;unsigned int dma_alignment;struct blk_queue_tag *queue_tags;struct list_head tag_busy_list;unsigned int nr_sorted;unsigned int in_flight[2];unsigned int rq_timeout;struct timer_list timeout;struct list_head timeout_list;struct queue_limits limits;/** sg stuff*/unsigned int sg_timeout;unsigned int sg_reserved_size;int node;
#ifdef CONFIG_BLK_DEV_IO_TRACEstruct blk_trace *blk_trace;
#endif/** reserved for flush operations*/unsigned int ordered, next_ordered, ordseq;int orderr, ordcolor;struct request pre_flush_rq, bar_rq, post_flush_rq;struct request *orig_bar_rq;struct mutex sysfs_lock;#if defined(CONFIG_BLK_DEV_BSG)struct bsg_class_device bsg_dev;
#endif
};
bio:块数据传送时怎样完成填充或读取块给driver
struct bio {sector_t bi_sector; /* device address in 512 bytesectors */struct bio *bi_next; /* request queue link */struct block_device *bi_bdev;unsigned long bi_flags; /* status, command, etc */unsigned long bi_rw; /* bottom bits READ/WRITE,* top bits priority*/unsigned short bi_vcnt; /* how many bio_vec's */unsigned short bi_idx; /* current index into bvl_vec *//* Number of segments in this BIO after* physical address coalescing is performed.*/unsigned int bi_phys_segments;unsigned int bi_size; /* residual I/O count *//** To keep track of the max segment size, we account for the* sizes of the first and last mergeable segments in this bio.*/unsigned int bi_seg_front_size;unsigned int bi_seg_back_size;unsigned int bi_max_vecs; /* max bvl_vecs we can hold */unsigned int bi_comp_cpu; /* completion CPU */atomic_t bi_cnt; /* pin count */struct bio_vec *bi_io_vec; /* the actual vec list */bio_end_io_t *bi_end_io;void *bi_private;
#if defined(CONFIG_BLK_DEV_INTEGRITY)struct bio_integrity_payload *bi_integrity; /* data integrity */
#endifbio_destructor_t *bi_destructor; /* destructor *//** We can inline a number of vecs at the end of the bio, to avoid* double allocations for a small number of bio_vecs. This member* MUST obviously be kept at the very end of the bio.*/struct bio_vec bi_inline_vecs[0];
};
gendisk:通用硬盘
struct gendisk {/* major, first_minor and minors are input parameters only,* don't use directly. Use disk_devt() and disk_max_parts().*/int major; /* major number of driver */int first_minor;int minors; /* maximum number of minors, =1 for* disks that can't be partitioned. */char disk_name[DISK_NAME_LEN]; /* name of major driver */char *(*devnode)(struct gendisk *gd, mode_t *mode);/* Array of pointers to partitions indexed by partno.* Protected with matching bdev lock but stat and other* non-critical accesses use RCU. Always access through* helpers.*/struct disk_part_tbl *part_tbl;struct hd_struct part0;const struct block_device_operations *fops;struct request_queue *queue;void *private_data;int flags;struct device *driverfs_dev; // FIXME: removestruct kobject *slave_dir;struct timer_rand_state *random;atomic_t sync_io; /* RAID */struct work_struct async_notify;
#ifdef CONFIG_BLK_DEV_INTEGRITYstruct blk_integrity *integrity;
#endifint node_id;
};
net
物理网卡:硬件网卡设备
虚拟网卡eth0等,fconfig查看网卡
网络命令ping、ifconfig等对网络API封装
socket、bind、listen、connect、send、recv等API对网络驱动封装
网络设备抽象为发送和接收数据包网络接口
结构体:net_device:网卡设备,sk_buff:内核缓冲区用于发送接收数据包
dm9000
module_init(dm9000_init);
module_exit(dm9000_cleanup);
static int __init dm9000_init(void)
{/* disable buzzer */s3c_gpio_setpull(S5PV210_GPD0(2), S3C_GPIO_PULL_UP);//设置上拉s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(1));//设置输出模式gpio_set_value(S5PV210_GPD0(2), 0);//设置输出值为0dm9000_power_int(); printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION);return platform_driver_register(&dm9000_driver);
}static void __exit dm9000_cleanup(void)
{platform_driver_unregister(&dm9000_driver);
}
static struct platform_driver dm9000_driver = {.driver = {.name = "dm9000",.owner = THIS_MODULE,.pm = &dm9000_drv_pm_ops,},.probe = dm9000_probe,.remove = __devexit_p(dm9000_drv_remove),
};
static int __devinit dm9000_probe(struct platform_device *pdev)
{struct dm9000_plat_data *pdata = pdev->dev.platform_data;struct board_info *db; /* Point a board information structure */struct net_device *ndev;const unsigned char *mac_src;int ret = 0;int iosize;int i;u32 id_val;/* Init network device */ndev = alloc_etherdev(sizeof(struct board_info));if (!ndev) {dev_err(&pdev->dev, "could not allocate device.\n");return -ENOMEM;}SET_NETDEV_DEV(ndev, &pdev->dev);dev_dbg(&pdev->dev, "dm9000_probe()\n");/* setup board info structure */db = netdev_priv(ndev);db->dev = &pdev->dev;db->ndev = ndev;spin_lock_init(&db->lock);mutex_init(&db->addr_lock);INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);if (db->addr_res == NULL || db->data_res == NULL ||db->irq_res == NULL) {dev_err(db->dev, "insufficient resources\n");ret = -ENOENT;goto out;}db->irq_wake = platform_get_irq(pdev, 1);if (db->irq_wake >= 0) {dev_dbg(db->dev, "wakeup irq %d\n", db->irq_wake);ret = request_irq(db->irq_wake, dm9000_wol_interrupt,IRQF_SHARED, dev_name(db->dev), ndev);//lqm changed irq method.//ret = request_irq(db->irq_wake, dm9000_wol_interrupt,IORESOURCE_IRQ_HIGHLEVEL, dev_name(db->dev), ndev);if (ret) {dev_err(db->dev, "cannot get wakeup irq (%d)\n", ret);} else {/* test to see if irq is really wakeup capable */ret = set_irq_wake(db->irq_wake, 1);if (ret) {dev_err(db->dev, "irq %d cannot set wakeup (%d)\n",db->irq_wake, ret);ret = 0;} else {set_irq_wake(db->irq_wake, 0);db->wake_supported = 1;}}}iosize = resource_size(db->addr_res);db->addr_req = request_mem_region(db->addr_res->start, iosize,pdev->name);if (db->addr_req == NULL) {dev_err(db->dev, "cannot claim address reg area\n");ret = -EIO;goto out;}db->io_addr = ioremap(db->addr_res->start, iosize);if (db->io_addr == NULL) {dev_err(db->dev, "failed to ioremap address reg\n");ret = -EINVAL;goto out;}iosize = resource_size(db->data_res);db->data_req = request_mem_region(db->data_res->start, iosize,pdev->name);if (db->data_req == NULL) {dev_err(db->dev, "cannot claim data reg area\n");ret = -EIO;goto out;}db->io_data = ioremap(db->data_res->start, iosize);if (db->io_data == NULL) {dev_err(db->dev, "failed to ioremap data reg\n");ret = -EINVAL;goto out;}/* fill in parameters for net-dev structure */ndev->base_addr = (unsigned long)db->io_addr;ndev->irq = db->irq_res->start;/* ensure at least we have a default set of IO routines */dm9000_set_io(db, iosize);/* check to see if anything is being over-ridden */if (pdata != NULL) {/* check to see if the driver wants to over-ride the* default IO width */if (pdata->flags & DM9000_PLATF_8BITONLY)dm9000_set_io(db, 1);if (pdata->flags & DM9000_PLATF_16BITONLY)dm9000_set_io(db, 2);if (pdata->flags & DM9000_PLATF_32BITONLY)dm9000_set_io(db, 4);/* check to see if there are any IO routine* over-rides */if (pdata->inblk != NULL)db->inblk = pdata->inblk;if (pdata->outblk != NULL)db->outblk = pdata->outblk;if (pdata->dumpblk != NULL)db->dumpblk = pdata->dumpblk;db->flags = pdata->flags;}#ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLLdb->flags |= DM9000_PLATF_SIMPLE_PHY;
#endifdm9000_reset(db);/* try multiple times, DM9000 sometimes gets the read wrong */for (i = 0; i < 8; i++) {id_val = ior(db, DM9000_VIDL);id_val |= (u32)ior(db, DM9000_VIDH) << 8;id_val |= (u32)ior(db, DM9000_PIDL) << 16;id_val |= (u32)ior(db, DM9000_PIDH) << 24;if (id_val == DM9000_ID)break;dev_err(db->dev, "read wrong id 0x%08x\n", id_val);}if (id_val != DM9000_ID) {dev_err(db->dev, "wrong id: 0x%08x\n", id_val);ret = -ENODEV;goto out;}/* Identify what type of DM9000 we are working on *//* I/O mode */db->io_mode = ior(db, DM9000_ISR) >> 6; /* ISR bit7:6 keeps I/O mode */ id_val = ior(db, DM9000_CHIPR);dev_dbg(db->dev, "dm9000 revision 0x%02x , io_mode %02x \n", id_val, db->io_mode);switch (id_val) {case CHIPR_DM9000A:db->type = TYPE_DM9000A;break;case 0x1a:db->type = TYPE_DM9000C;break;default:dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val);db->type = TYPE_DM9000E;}/* from this point we assume that we have found a DM9000 *//* driver system function */ether_setup(ndev);ndev->netdev_ops = &dm9000_netdev_ops;ndev->watchdog_timeo = msecs_to_jiffies(watchdog);ndev->ethtool_ops = &dm9000_ethtool_ops;db->msg_enable = NETIF_MSG_LINK;db->mii.phy_id_mask = 0x1f;db->mii.reg_num_mask = 0x1f;db->mii.force_media = 0;db->mii.full_duplex = 0;db->mii.dev = ndev;db->mii.mdio_read = dm9000_phy_read;db->mii.mdio_write = dm9000_phy_write;mac_src = "eeprom";/* try reading the node address from the attached EEPROM */for (i = 0; i < 6; i += 2)dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {mac_src = "platform data";//memcpy(ndev->dev_addr, pdata->dev_addr, 6);/* mac from bootloader */memcpy(ndev->dev_addr, mac, 6);}if (!is_valid_ether_addr(ndev->dev_addr)) {/* try reading from mac */mac_src = "chip";for (i = 0; i < 6; i++)ndev->dev_addr[i] = ior(db, i+DM9000_PAR);}if (!is_valid_ether_addr(ndev->dev_addr))dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please ""set using ifconfig\n", ndev->name);platform_set_drvdata(pdev, ndev);ret = register_netdev(ndev);if (ret == 0)printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",ndev->name, dm9000_type_to_char(db->type),db->io_addr, db->io_data, ndev->irq,ndev->dev_addr, mac_src);return 0;out:dev_err(db->dev, "not found (%d).\n", ret);dm9000_release_board(pdev, db);free_netdev(ndev);return ret;
}
struct platform_device s5p_device_dm9000 = {.name = "dm9000",.id = 0,.num_resources = ARRAY_SIZE(s5p_dm9000_resources),.resource = s5p_dm9000_resources,.dev = {.platform_data = &s5p_dm9000_platdata,}
};
demo1:
虚拟块设备驱动
blockdev.c
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/dma.h>#define RAMBLOCK_SIZE (1024*1024) // 1MB,2048扇区static struct gendisk *my_ramblock_disk; // 磁盘设备的结构体
static struct request_queue *my_ramblock_queue; // 等待队列
static DEFINE_SPINLOCK(my_ramblock_lock);
static int major;
static unsigned char *my_ramblock_buf; // 虚拟块设备的内存指针static void do_my_ramblock_request(struct request_queue *q)
{struct request *req;static int r_cnt = 0; //实验用,打印出驱动读与写的调度方法static int w_cnt = 0;req = blk_fetch_request(q);while (NULL != req){unsigned long start = blk_rq_pos(req) *512;unsigned long len = blk_rq_cur_bytes(req);if(rq_data_dir(req) == READ){// 读请求memcpy(req->buffer, my_ramblock_buf + start, len); //读操作,printk("do_my_ramblock-request read %d times\n", r_cnt++);}else{// 写请求memcpy( my_ramblock_buf+start, req->buffer, len); //写操作printk("do_my_ramblock request write %d times\n", w_cnt++);}if(!__blk_end_request_cur(req, 0)) {req = blk_fetch_request(q);}}
}static int blk_ioctl(struct block_device *dev, fmode_t no, unsigned cmd, unsigned long arg)
{return -ENOTTY;
}static int blk_open (struct block_device *dev , fmode_t no)
{printk("blk mount succeed\n");return 0;
}
static int blk_release(struct gendisk *gd , fmode_t no)
{printk("blk umount succeed\n");return 0;
}static const struct block_device_operations my_ramblock_fops =
{.owner = THIS_MODULE,.open = blk_open,.release = blk_release,.ioctl = blk_ioctl,
};static int my_ramblock_init(void)
{major = register_blkdev(0, "my_ramblock");if (major < 0){printk("fail to regiser my_ramblock\n");return -EBUSY;}// 实例化my_ramblock_disk = alloc_disk(1); //次设备个数 ,分区个数 +1//分配设置请求队列,提供读写能力my_ramblock_queue = blk_init_queue(do_my_ramblock_request, &my_ramblock_lock);//设置硬盘属性 my_ramblock_disk->major = major;my_ramblock_disk->first_minor = 0;my_ramblock_disk->fops = &my_ramblock_fops;sprintf(my_ramblock_disk->disk_name, "my_ramblcok"); // /dev/namemy_ramblock_disk->queue = my_ramblock_queue;set_capacity(my_ramblock_disk, RAMBLOCK_SIZE / 512);/* 硬件相关操作 */my_ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL);add_disk(my_ramblock_disk); // 向驱动框架注册一个disk或者一个partation的接口return 0;
}static void my_ramblock_exit(void)
{unregister_blkdev(major, "my_ramblock");del_gendisk(my_ramblock_disk);put_disk(my_ramblock_disk);blk_cleanup_queue(my_ramblock_queue);kfree(my_ramblock_buf);
}module_init(my_ramblock_init);
module_exit(my_ramblock_exit);
MODULE_LICENSE("GPL");
Makefile
KERN_DIR = /root/kernelobj-m += blockdev.oall:make -C $(KERN_DIR) M=`pwd` modules
cp:cp *.ko /root/rootfs/driver -f.PHONY: clean
clean:make -C $(KERN_DIR) M=`pwd` modules clean
测试示例:
demo2:
虚拟网卡驱动
net.c
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/interrupt.h> /* mark_bh */
#include <linux/in.h>
#include <linux/netdevice.h> /* struct device, and other headers */
#include <linux/etherdevice.h> /* eth_type_trans */
#include <linux/ip.h> /* struct iphdr */
#include <linux/tcp.h> /* struct tcphdr */
#include <linux/skbuff.h>
#include <linux/if_ether.h>
#include <linux/in6.h>
#include <asm/uaccess.h>
#include <asm/checksum.h>
#include <linux/platform_device.h>// 如果需要随机MAC地址则定义该宏
#define MAC_AUTOstatic struct net_device *cbxnet_devs;//网络设备结构体,作为net_device->priv
struct cbxnet_priv {struct net_device_stats stats; //有用的统计信息int status; //网络设备的状态信息,是发完数据包,还是接收到网络数据包int rx_packetlen; //接收到的数据包长度u8 *rx_packetdata; //接收到的数据int tx_packetlen; //发送的数据包长度u8 *tx_packetdata; //发送的数据struct sk_buff *skb; //socket buffer结构体,网络各层之间传送数据都是通过这个结构体来实现的spinlock_t lock; //自旋锁
};//网络接口的打开函数
int cbxnet_open(struct net_device *dev)
{printk("cbxnet_open\n");#ifndef MAC_AUTOint i;for (i=0; i<6; i++)dev->dev_addr[i] = 0xaa;
#elserandom_ether_addr(dev->dev_addr); //随机源地址
#endifnetif_start_queue(dev); //打开传输队列,这样才能进行数据传输return 0;
}int cbxnet_release(struct net_device *dev)
{printk("cbxnet_release\n");//当网络接口关闭的时候,调用stop方法,这个函数表示不能再发送数据netif_stop_queue(dev);return 0;
}//接包函数
void cbxnet_rx(struct net_device *dev, int len, unsigned char *buf)
{ struct sk_buff *skb;struct cbxnet_priv *priv = (struct cbxnet_priv *) dev->ml_priv;skb = dev_alloc_skb(len+2);//分配一个socket buffer,并且初始化skb->data,skb->tail和skb->headif (!skb) {printk("gecnet rx: low on mem - packet dropped\n");priv->stats.rx_dropped++;return;}skb_reserve(skb, 2); /* align IP on 16B boundary */ memcpy(skb_put(skb, len), buf, len);//skb_put是把数据写入到socket buffer/* Write metadata, and then pass to the receive level */skb->dev = dev;skb->protocol = eth_type_trans(skb, dev);//返回的是协议号skb->ip_summed = CHECKSUM_UNNECESSARY; //此处不校验priv->stats.rx_packets++;//接收到包的个数+1priv->stats.rx_bytes += len;//接收到包的长度printk("cbxnet rx \n");netif_rx(skb);//通知内核已经接收到包,并且封装成socket buffer传到上层return;
}//真正的处理的发送数据包
//模拟从一个网络向另一个网络发送数据包
void cbxnet_hw_tx(char *buf, int len, struct net_device *dev){struct net_device *dest;//目标设备结构体,net_device存储一个网络接口的重要信息,是网络驱动程序的核心struct cbxnet_priv *priv;if (len < sizeof(struct ethhdr) + sizeof(struct iphdr)) {printk("cbxnet: Hmm... packet too short (%i octets)\n", len);return;}dest = cbxnet_devs;priv = (struct cbxnet_priv *)dest->ml_priv; //目标dest中的privpriv->rx_packetlen = len;priv->rx_packetdata = buf;printk("cbxnet tx \n");dev_kfree_skb(priv->skb);
}//发包函数
int cbxnet_tx(struct sk_buff *skb, struct net_device *dev)
{int len;char *data;struct cbxnet_priv *priv = (struct cbxnet_priv *)dev->ml_priv;if (skb == NULL){printk("net_device %p, skb %p\n", dev, skb);return 0;}len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;//ETH_ZLEN是所发的最小数据包的长度data = skb->data;//将要发送的数据包中数据部分priv->skb = skb;cbxnet_hw_tx(data, len, dev);//真正的发送函数return 0;
}//设备初始化函数
int cbxnet_init(struct net_device *dev)
{printk("cbxcnet_init\n");ether_setup(dev);//填充一些以太网中的设备结构体的项/* keep the default flags, just add NOARP */dev->flags |= IFF_NOARP;//为priv分配内存dev->ml_priv = kmalloc(sizeof(struct cbxnet_priv), GFP_KERNEL);if (dev->ml_priv == NULL)return -ENOMEM;memset(dev->ml_priv, 0, sizeof(struct cbxnet_priv));spin_lock_init(&((struct cbxnet_priv *)dev->ml_priv)->lock);return 0;
}static const struct net_device_ops cbxnet_netdev_ops = {.ndo_open = cbxnet_open, // 打开网卡 对应 ifconfig xx up.ndo_stop = cbxnet_release, // 关闭网卡 对应 ifconfig xx down.ndo_start_xmit = cbxnet_tx, // 开启数据包传输.ndo_init = cbxnet_init, // 初始化网卡硬件
};static void cbx_plat_net_release(struct device *dev)
{printk("cbx_plat_net_release\n");
}static int __devinit cbx_net_probe(struct platform_device *pdev)
{int result=0;cbxnet_devs = alloc_etherdev(sizeof(struct net_device));cbxnet_devs->netdev_ops = &cbxnet_netdev_ops;strcpy(cbxnet_devs->name, "cbxnet0");if ((result = register_netdev(cbxnet_devs)))printk("cbxnet: error %i registering device \"%s\"\n", result, cbxnet_devs->name);return 0;
}static int __devexit cbx_net_remove(struct platform_device *pdev) //设备移除接口
{kfree(cbxnet_devs->ml_priv);unregister_netdev(cbxnet_devs);return 0;
}static struct platform_device cbx_net= {.name = "cbx_net",.id = -1,.dev = {.release = cbx_plat_net_release,},
};static struct platform_driver cbx_net_driver = {.probe = cbx_net_probe, .remove = __devexit_p(cbx_net_remove), .driver = {.name ="cbx_net",.owner = THIS_MODULE,},
};static int __init cbx_net_init(void)
{printk("cbx_net_init \n");platform_device_register(&cbx_net);return platform_driver_register(&cbx_net_driver );
}static void __exit cbx_net_cleanup(void)
{platform_driver_unregister(&cbx_net_driver ); platform_device_unregister(&cbx_net);
}module_init(cbx_net_init);
module_exit(cbx_net_cleanup);
MODULE_LICENSE("GPL");
Makefile
KERN_DIR = /root/kernelobj-m += net.oall:make -C $(KERN_DIR) M=`pwd` modules
cp:cp *.ko /root/rootfs/driver -f.PHONY: clean
clean:make -C $(KERN_DIR) M=`pwd` modules clean
测试示例: