linux驱动(八):block,net

news2025/1/12 15:50:45

        本文主要探讨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_SYSFS
	struct list_head	bd_holder_list;
#endif
	struct 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_REQUEST
	int make_it_fail;
#endif
	unsigned long stamp;
	int in_flight[2];
#ifdef	CONFIG_SMP
	struct disk_stats __percpu *dkstats;
#else
	struct disk_stats dkstats;
#endif
	struct 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_CGROUP
	unsigned 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_TRACE
	struct 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 byte
						   sectors */
	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 */
#endif

	bio_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: remove
	struct kobject *slave_dir;

	struct timer_rand_state *random;

	atomic_t sync_io;		/* RAID */
	struct work_struct async_notify;
#ifdef  CONFIG_BLK_DEV_INTEGRITY
	struct blk_integrity *integrity;
#endif
	int 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);//设置输出值为0

	dm9000_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_POLL
	db->flags |= DM9000_PLATF_SIMPLE_PHY;
#endif

	dm9000_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/name
    my_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/kernel
 
obj-m   += blockdev.o
 
all:
         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_AUTO


static 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_AUTO
        int i;
        for (i=0; i<6; i++)
            dev->dev_addr[i] = 0xaa;
#else
    random_ether_addr(dev->dev_addr); //随机源地址
#endif
    netif_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->head
    if (!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++;//接收到包的个数+1
    
    priv->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中的priv
    priv->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/kernel
 
obj-m   += net.o
 
all:
         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

测试示例:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1401579.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode的AC指南 —— 栈与队列:20. 有效的括号

摘要&#xff1a; **Leetcode的AC指南 —— 栈与队列&#xff1a;20. 有效的括号 **。题目介绍&#xff1a;给定一个只包括 ‘(’&#xff0c;‘)’&#xff0c;‘{’&#xff0c;‘}’&#xff0c;‘[’&#xff0c;‘]’ 的字符串 s &#xff0c;判断字符串是否有效。 有效字…

基于JavaWeb+SSM+Vue智能社区服务小程序系统的设计和实现

基于JavaWebSSMVue智能社区服务小程序系统的设计和实现 滑到文末获取源码Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 滑到文末获取源码 Lun文目录 目录 1系统概述 1 1.1 研究背景 1 1.2研究目的 1 1.3系统设计思想 1 2相…

【计算机硬件】4、练习题

根据冯诺依曼原理&#xff0c;计算机硬件的基本组成是&#xff08;&#xff09; A.输入、输出设备、运算器、控制器、存储器 B.磁盘、软盘、内存、CPU、显示 C.打印机、触摸屏、键盘、软盘 D.鼠标、打印机、主机、显示器、存储器 CPU执行算术运算或者逻辑运算时&#xff0c;常…

class_14:继承

C继承有点类似于c语言 结构体套用 #include <iostream> #include <string> using namespace std;//基类,父类 class Vehicle{ public:string type;string contry;string color;double price;int numOfWheel;void run();void stop(); };//派生类&#xff0c…

Python基础第五篇(Python数据容器)

文章目录 一、数据容器入门二、数据容器 list 列表(1),list 列表定义(2),list列表的索引(3),list列表的常见操作(4),list列表的遍历 三、数据容器&#xff1a;tuple&#xff08;元组&#xff09;(1),tuple元组定义(2),tuple元组的索引(3),tuple元组的常见操作(4),tuple元组的遍…

力扣【四数之和】

一、题目描述 18. 四数之和 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&#xff0c;则认为两个四元组重复&#x…

PyQt5 快速入门(一)

第一节按钮控件,文本控件,输入框,app图标 文章目录 一.GUI按钮控件 二.文本控件 三.输入框 四.让窗口显示在屏幕中央 五.让窗口显示在屏幕中央 总结 一.GUI按钮控件 import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButtonif __name__ __main__:app …

python爬虫知识点:5种线程锁

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 线程安全 线程安全是多线程或多进程编程中的一个概念&#xff0c;在拥有共享数据的多条线程并行执行的程序中&#xff0c;线程安全的代码会通过同步机制保证各个…

Python使用graphviz绘制模块间数据流

graphviz官方参考链接&#xff1a; http://www.graphviz.org/documentation/ https://graphviz.readthedocs.io/en/stable/index.html 文章目录 需求描述环境配置实现思路代码实现 需求描述 根据各模块之间的传参关系绘制出数据流&#xff0c;如下图所示&#xff1a; 并且生成…

Redis(01)——常用指令

基础指令 select 数字&#xff1a;切换到其他数据库flushdb&#xff1a;清空当前数据库flushall&#xff1a;清空所有数据库dbsize&#xff1a;查看数据库大小exists key1[key2 …]&#xff1a;判断当前的key是否存在keys *&#xff1a;查看所有的keyexpire key 时间&#xff…

深入Matplotlib:画布分区与高级图形展示【第33篇—python:Matplotlib】

文章目录 Matplotlib画布分区技术详解引言方法一&#xff1a;plt.subplot()方法二&#xff1a;简略写法方法三&#xff1a;plt.subplots()实例展示添加更多元素 进一步探索Matplotlib画布分区自定义子图布局3D子图结语 Matplotlib画布分区技术详解 引言 Matplotlib是一个强大…

第13节-简历中的开放性问题

(点击即可收听) 不少公司的开放式题目每年不会有太大的变化&#xff0c;所以在答题前可先去相关求职论坛看看这些公司往年的问题&#xff0c;分析和思考自己应当怎么回答 开放式问题回答技巧 开放式问题主要考察的是求职者的求职动机、解决问题的能力、创造力等软实力&#xff…

C++入门之基础语法

目录 一.关键字 二.命名空间 2.1命名空间域 2.2展开命名空间域 using namespace bit 使用using将命名空间中的某个成员引入 2.3 头文件#include 2.3.1 头文件的展开和命名空间的展开区别 2.4 C的标准的库命名空间std 2.5 命名空间的套娃 三. C输入输出 3.1 流插入co…

线性代数:逆矩阵

目录 伴随阵 逆矩阵 证明&#xff1a;AA* A*A |A|E 证明&#xff1a;|A| 0 > |A*| 0 伴随阵 逆矩阵 证明&#xff1a;AA* A*A |A|E 证明&#xff1a;|A| 0 > |A*| 0

项目管理该考哪个证书❓NPDP还是软考❓

有小伙伴在纠结是要考NPDP认证呢还是考软考呢❓ 今天小编要给大家好好说说NPDP认证❗️ &#x1f4a1;NPDP全称New Product Development Professional&#xff0c;也就是产品经理国际资格认证。 &#x1f525;NPDP是国际公认的为一的新产品开发专业认证&#xff0c;是集理论、方…

JVM系列-2.字节码文件详解

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术、JVM原理&#x1f525;如果感觉博主的文…

(超详细)6-YOLOV5改进-添加ECA注意力机制

1、在yolov5/models下面新建一个EfficientChannelAttention.py文件&#xff0c;在里面放入下面的代码 代码如下&#xff1a; import torch, math from torch import nnclass EfficientChannelAttention(nn.Module): # Efficient Channel Attention moduledef __ini…

uniCloud uni-id体系的使用

目录 简介 uni-id导入和配置 用户表与文章表关联foreignKey 字段级权限控制 指定数据集权限控制 权限规则的变量和运算符 简介 uni-id已完成的功能&#xff1a; 注册、登录、发送短信验证码、密码加密保存、修改密码、忘记密码、头像管理、token管理、rbac权限角色体系、…

CVPR 2023 Hybrid Tutorial: All Things ViTs之mean attention distance (MAD)

All Things ViTs系列讲座从ViT视觉模型注意力机制出发,本文给出mean attention distance可视化部分阅读学习体会. 课程视频与课件: https://all-things-vits.github.io/atv/ 代码: https://colab.research.google.com/github/all-things-vits/code-samples/blob/main/probing/m…

2024年【河北省安全员B证】最新解析及河北省安全员B证试题及解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 河北省安全员B证最新解析是安全生产模拟考试一点通生成的&#xff0c;河北省安全员B证证模拟考试题库是根据河北省安全员B证最新版教材汇编出河北省安全员B证仿真模拟考试。2024年【河北省安全员B证】最新解析及河北省…