linux scatterlist阅读二

#include <linux/init.h>    
#include <linux/module.h>    
#include <linux/kernel.h>    
#include <linux/fs.h>  
#include <asm/uaccess.h>  
#include <linux/spinlock.h>  
#include <linux/sched.h>  
#include <linux/types.h>  
#include <linux/fcntl.h>  
#include <linux/hdreg.h>  
#include <linux/genhd.h>  
#include <linux/blkdev.h>  
#include <linux/scatterlist.h>

//struct sg_table sgt;

struct scatterlist *sgl;
void *buf;

/* 驱动出口函数 */
static int __init ramdisk_init(void)
{
	int i;
	struct scatterlist *sg;
	printk("sg init\n");
	
	sgl = (struct scatterlist *)kmalloc(sizeof(struct scatterlist) * 1, GFP_KERNEL);
	if(!sgl)
		return -1;
	sg_init_table(sgl, 1);

	buf = kmalloc(1000, GFP_KERNEL);
	if(!buf)
		return -1;
	printk("buf = %p\n", buf);
	sg_init_one(sgl, buf, 1000);
	for_each_sg(sgl, sg, 1, i) {
		dma_addr_t phys = sg_phys(sg);
		pr_warn("sg[%d] phys_addr:%pad offset:%d length:%d "
			"dma_address:%pad dma_length:%d\n",
			i, &phys, sg->offset, sg->length, &sg_dma_address(sg),
			sg_dma_len(sg));
	}
	return 0;
}

/* 驱动出口函数 */
static void __exit ramdisk_exit(void)
{
	printk("sg exit\n");
	kfree(buf);
	kfree(sgl);
}

module_init(ramdisk_init);
module_exit(ramdisk_exit);

MODULE_AUTHOR("Tan xujia");
MODULE_LICENSE("Dual BSD/GPL");

在这里插入图片描述

sg_init_table

可以看到这个函数的作用就是将sgl起始地址的一段空间清零,然后设置最后一个元素的page_link 值,由于是最后一个元素,所以page_link 的第一个bit被设置为1。

static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents)
{
	memset(sgl, 0, sizeof(*sgl) * nents);
	sg_mark_end(&sgl[nents - 1]);
}

static inline void sg_mark_end(struct scatterlist *sg)
{
	/*
	 * Set termination bit, clear potential chain bit
	 */
	sg->page_link |= SG_END; //0x02
	sg->page_link &= ~SG_CHAIN; //0x01
}

sg_init_one

这个函数的作用,实现就是调用sg_init_table,然后使用sg_set_buf这个设置
page_link ,offset,length的值,其中page_link 保存的是经过转换以后的物理地址,在使用sg_init_one函数的例子当中,buf传入的是内核虚拟地址。

/**
 * sg_init_one - Initialize a single entry sg list
 * @sg:		 SG entry
 * @buf:	 Virtual address for IO
 * @buflen:	 IO length
 *
 **/
void sg_init_one(struct scatterlist *sg, const void *buf, unsigned int buflen)
{
	sg_init_table(sg, 1);
	sg_set_buf(sg, buf, buflen);
}
EXPORT_SYMBOL(sg_init_one);

/**
 * sg_set_buf - Set sg entry to point at given data
 * @sg:		 SG entry
 * @buf:	 Data
 * @buflen:	 Data length
 *
 **/
static inline void sg_set_buf(struct scatterlist *sg, const void *buf,
			      unsigned int buflen)
{
#ifdef CONFIG_DEBUG_SG
	BUG_ON(!virt_addr_valid(buf));
#endif
	sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf));
}

/**
 * sg_set_page - Set sg entry to point at given page
 * @sg:		 SG entry
 * @page:	 The page
 * @len:	 Length of data
 * @offset:	 Offset into page
 *
 * Description:
 *   Use this function to set an sg entry pointing at a page, never assign
 *   the page directly. We encode sg table information in the lower bits
 *   of the page pointer. See sg_page() for looking up the page belonging
 *   to an sg entry.
 *
 **/
static inline void sg_set_page(struct scatterlist *sg, struct page *page,
			       unsigned int len, unsigned int offset)
{
	sg_assign_page(sg, page);
	sg->offset = offset;
	sg->length = len;
}

/**
 * sg_assign_page - Assign a given page to an SG entry
 * @sg:		    SG entry
 * @page:	    The page
 *
 * Description:
 *   Assign page to sg entry. Also see sg_set_page(), the most commonly used
 *   variant.
 *
 **/
static inline void sg_assign_page(struct scatterlist *sg, struct page *page)
{
	unsigned long page_link = sg->page_link & (SG_CHAIN | SG_END);

	/*
	 * In order for the low bit stealing approach to work, pages
	 * must be aligned at a 32-bit boundary as a minimum.
	 */
	BUG_ON((unsigned long) page & (SG_CHAIN | SG_END));
#ifdef CONFIG_DEBUG_SG
	BUG_ON(sg_is_chain(sg));
#endif
	sg->page_link = page_link | (unsigned long) page;
}

而像dma_length,dma_address一类成员的值的设置就要使用类似dma_map_sg的函数去设置了,设置以后,记录的就是设备认识的dma地址了。此时dma_length,dma_address应该有值,而不是0.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值