获取物理地址空间驱动,支持多个子驱动,Example: insmod gphyaddr.ko varm=1:4M,2:5K。
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/slab.h> #define MAX_DEV_NUM 32 #define GET_PYH_SPACE_SIZE 0 #define GET_PYHSICAL_ADDR 1 struct gphyaddr_dev { int dev_num; dev_t dev; struct class *cls; struct cdev cdev; int index[MAX_DEV_NUM]; unsigned long space_size[MAX_DEV_NUM]; unsigned int p_addr[MAX_DEV_NUM]; unsigned int order[MAX_DEV_NUM]; unsigned long v_addr[MAX_DEV_NUM]; }; static struct gphyaddr_dev * g_dev = NULL; static char * vram="1:4M"; module_param(vram, charp, 0644); MODULE_PARM_DESC(vram,"A string variable"); static int gphyaddr_open(struct inode *inode, struct file *file) { struct gphyaddr_dev * dev; dev = container_of(inode->i_cdev, struct gphyaddr_dev, cdev); file->private_data = dev; return 0; } static long gphyaddr_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct gphyaddr_dev * dev = file->private_data; struct inode *inode = file->f_path.dentry->d_inode; int minor = MINOR(inode->i_rdev); //printk("%s:minor: %d. ", __func__, minor); //printk("p_addr: 0x%x. ", dev->p_addr[minor]); //printk("v_addr: 0x%lx, order: %d. ", dev->v_addr[minor], dev->order[minor]); switch (cmd) { case GET_PYH_SPACE_SIZE: return (long)(dev->order[minor] * PAGE_SIZE); case GET_PYHSICAL_ADDR: return (long)dev->v_addr[minor]; default: printk("Parameter error!! "); return -1; } return 0; } struct file_operations gphyaddr_fops = { .owner = THIS_MODULE, .open = gphyaddr_open, .unlocked_ioctl = gphyaddr_ioctl, }; static void gphyaddr_exit(void) { int index, i; printk("gphyaddr_exit. "); if (g_dev) { for(i = 0; i < g_dev->dev_num; i++) { index = g_dev->index[i]; device_destroy(g_dev->cls, MKDEV(MAJOR(g_dev->dev), index)); if(g_dev->v_addr[index] > 0) { free_pages(g_dev->v_addr[index], g_dev->order[index]); } } if(NULL != g_dev->cls) { class_destroy(g_dev->cls); } if(g_dev->cdev.count > 0) { cdev_del(&g_dev->cdev); } if(g_dev->dev_num > 0) { unregister_chrdev_region(g_dev->dev, g_dev->dev_num); } kfree(g_dev); } } static int get_phy_addr(struct gphyaddr_dev * g_dev, unsigned int space_size, int index) { char *v_addr; unsigned int p_addr,order; order = get_order(space_size); if(order >= MAX_ORDER) { printk("ERROR: order(%u) >= MAX_ORDER(%u)!!! ", order, MAX_ORDER); order = MAX_ORDER - 1; } v_addr = (char *)__get_free_pages(GFP_KERNEL, order); if(v_addr == NULL) { printk("ERROR: get_free_pages err!!! "); return -EFAULT; } p_addr = virt_to_phys(v_addr); printk("index= %d, order= %d, space_size= 0x%lx, p_addr: 0x%x ", index, order, order * PAGE_SIZE, p_addr); g_dev->p_addr[index] = p_addr; g_dev->order[index] = order; g_dev->v_addr[index] = (unsigned long)v_addr; return 0; } static int parse_vram_param(struct gphyaddr_dev * g_dev, const char *param, int max_dev_num) { int i = 0; unsigned long size; char *p, *start; start = (char *)param; g_dev->dev_num = 0; while (1) { p = start; g_dev->index[i] = simple_strtoul(p, &p, 10); if (p == param) goto err; if (*p != ':') goto err; if (g_dev->index[i] > max_dev_num) goto err; size = memparse(p + 1, &p); if (!size) goto err; printk("i: %d index: %d size: 0x%x ",i, g_dev->index[i], (u32)size); g_dev->space_size[g_dev->index[i]] = size; g_dev->dev_num++; i++; if (*p == 0) break; if (*p != ',') goto err; ++p; start = p; } return 0; err: printk("Input format error!! Example: insmod gphyaddr.ko varm=1:4M,2:5K "); return -EINVAL; } static int gphyaddr_init(void) { int ret, index, i; g_dev = kmalloc(sizeof(struct gphyaddr_dev), GFP_KERNEL); if (!g_dev) { printk("Error: kmalloc ERR. "); ret = -ENOMEM; goto fail; } memset(g_dev, 0, sizeof(struct gphyaddr_dev)); if (parse_vram_param(g_dev, vram, MAX_DEV_NUM)) { printk("Error: failed to parse vram parameters: %s ", vram); ret = -ENOMEM; goto fail; } alloc_chrdev_region(&g_dev->dev, 0, g_dev->dev_num, "gphyaddr"); cdev_init(&g_dev->cdev, &gphyaddr_fops); g_dev->cdev.owner = THIS_MODULE; g_dev->cdev.ops = &gphyaddr_fops; ret = cdev_add (&g_dev->cdev, g_dev->dev, g_dev->dev_num);//添加字符设备 if(ret) { printk("Error: %d adding gphyaddr. ", ret); goto fail; } g_dev->cls = class_create(THIS_MODULE, "gphyaddr"); for(i = 0; i < g_dev->dev_num; i++) { index = g_dev->index[i]; ret = get_phy_addr(g_dev, g_dev->space_size[index], index); if(ret) { printk("Error: %d get_phy_addr!! ", ret); goto fail; } device_create(g_dev->cls, NULL, MKDEV(MAJOR(g_dev->dev), index), NULL, "gphyaddr%d", index); } return 0; fail: gphyaddr_exit(); return ret; } module_init(gphyaddr_init); module_exit(gphyaddr_exit); MODULE_LICENSE("GPL");