#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.