字符設(shè)備是3大類設(shè)備(字符設(shè)備、塊設(shè)備和網(wǎng)絡(luò)設(shè)備)中較簡(jiǎn)單的一類設(shè)備,其驅(qū)動(dòng)程序中完成的主要工作是初始化、添加和刪除cdev結(jié)構(gòu)體,申請(qǐng)和釋放設(shè)備號(hào),以及填充 file_operations結(jié)構(gòu)體中的操作函數(shù),實(shí)現(xiàn)file_operations結(jié)構(gòu)體中的read()、write()和ioctl()等函數(shù)是驅(qū)動(dòng)設(shè)計(jì)的主體工作。
參考例程源碼
/* * 虛擬字符設(shè)備globalmem實(shí)例: * 在globalmem字符設(shè)備驅(qū)動(dòng)中會(huì)分配一片大小為 GLOBALMEM_SIZE(4KB) * 的內(nèi)存空間,并在驅(qū)動(dòng)中提供針對(duì)該片內(nèi)存的讀寫、控制和定位函數(shù),以供用戶空間的進(jìn)程能通過 * Linux系統(tǒng)調(diào)用訪問這片內(nèi)存。 */#include #include #include #include #include #include #include #include #include #include #include #define DEV_NAME "globalmem" /* /dev中顯示的設(shè)備名 */#define DEV_MAJOR 0 /* 指定主設(shè)備號(hào),為0則動(dòng)態(tài)獲取 *//* ioctl用的控制字 */#define GLOBALMEM_MAGIC 'M'#define MEM_CLEAR _IO(GLOBALMEM_MAGIC, 0)/*--------------------------------------------------------------------- local vars *//*globalmem設(shè)備結(jié)構(gòu)體*/typedef struct { struct cdev cdev; /* 字符設(shè)備cdev結(jié)構(gòu)體*/#define MEM_SIZE 0x1000 /*全局內(nèi)存最大4K字節(jié)*/ unsigned char mem[MEM_SIZE]; /*全局內(nèi)存*/ struct semaphore sem; /*并發(fā)控制用的信號(hào)量*/} globalmem_dev_t;static int globalmem_major = DEV_MAJOR;static globalmem_dev_t *globalmem_devp; /*設(shè)備結(jié)構(gòu)體指針*//*--------------------------------------------------------------------- file operations *//*文件打開函數(shù)*/static int globalmem_open(struct inode *inodep, struct file *filep){ /* 獲取dev指針 */ globalmem_dev_t *dev = container_of(inodep->i_cdev, globalmem_dev_t, cdev); filep->private_data = dev; return 0;}/*文件釋放函數(shù)*/static int globalmem_release(struct inode *inodep, struct file *filep){ return 0;}/*讀函數(shù)*/static ssize_t globalmem_read(struct file *filep, char __user *buf, size_t len, loff_t *ppos){ globalmem_dev_t *dev = filep->private_data; unsigned long p = *ppos; int ret = 0; /*分析和獲取有效的長度*/ if (p > MEM_SIZE) { printk(KERN_EMERG "%s: overflow!\n", __func__); return - ENOMEM; } if (len > MEM_SIZE - p) { len = MEM_SIZE - p; } if (down_interruptible(&dev->sem)) /* 獲得信號(hào)量*/ return - ERESTARTSYS; /*內(nèi)核空間->用戶空間*/ if (copy_to_user(buf, (void*)(dev->mem + p), len)) { ret = - EFAULT; }else{ *ppos += len; printk(KERN_INFO "%s: read %d bytes from %d\n", DEV_NAME, (int)len, (int)p); ret = len; } up(&dev->sem); /* 釋放信號(hào)量*/ return ret;}/*寫函數(shù)*/static ssize_t globalmem_write(struct file *filep, const char __user *buf, size_t len, loff_t *ppos){ globalmem_dev_t *dev = filep->private_data; int ret = 0; unsigned long p = *ppos; if (p > MEM_SIZE) { printk(KERN_EMERG "%s: overflow!\n", __func__); return - ENOMEM; } if (len > MEM_SIZE - p) { len = MEM_SIZE - p; } if (down_interruptible(&dev->sem)) /* 獲得信號(hào)量*/ return - ERESTARTSYS; /*用戶空間->內(nèi)核空間*/ if (copy_from_user(dev->mem + p, buf, len)) { ret = - EFAULT; }else{ *ppos += len; printk(KERN_INFO "%s: written %d bytes from %d\n", DEV_NAME, (int)len, (int)p); ret = len; } up(&dev->sem); /* 釋放信號(hào)量*/ return ret;}/* seek文件定位函數(shù) */static loff_t globalmem_llseek(struct file *filep, loff_t offset, int start){ globalmem_dev_t *dev = filep->private_data; int ret = 0; if (down_interruptible(&dev->sem)) /* 獲得信號(hào)量*/ return - ERESTARTSYS; switch (start) { case SEEK_SET: if (offset < 0 || offset > MEM_SIZE) { printk(KERN_EMERG "%s: overflow!\n", __func__); return - ENOMEM; } ret = filep->f_pos = offset; break; case SEEK_CUR: if ((filep->f_pos + offset) < 0 || (filep->f_pos + offset) > MEM_SIZE) { printk(KERN_EMERG "%s: overflow!\n", __func__); return - ENOMEM; } ret = filep->f_pos += offset; break; default: return - EINVAL; break; } up(&dev->sem); /* 釋放信號(hào)量*/ printk(KERN_INFO "%s: set cur to %d.\n", DEV_NAME, ret); return ret;}/* ioctl設(shè)備控制函數(shù) */static long globalmem_ioctl(struct file *filep, unsigned int cmd, unsigned long arg){ globalmem_dev_t *dev = filep->private_data; switch (cmd) { case MEM_CLEAR: if (down_interruptible(&dev->sem)) /* 獲得信號(hào)量*/ return - ERESTARTSYS; memset(dev->mem, 0, MEM_SIZE); up(&dev->sem); /* 釋放信號(hào)量*/ printk(KERN_INFO "%s: clear.\n", DEV_NAME); break; default: return - EINVAL; } return 0;}/*文件操作結(jié)構(gòu)體*/static const struct file_operations globalmem_fops = { .owner = THIS_MODULE, .open = globalmem_open, .release = globalmem_release, .read = globalmem_read, .write = globalmem_write, .llseek = globalmem_llseek, .compat_ioctl = globalmem_ioctl};/*---------------------------------------------------------------------*//*初始化并注冊(cè)cdev*/static int globalmem_setup(globalmem_dev_t *dev, int minor){ int ret = 0; dev_t devno = MKDEV(globalmem_major, minor); cdev_init(&dev->cdev, &globalmem_fops); dev->cdev.owner = THIS_MODULE; ret = cdev_add(&dev->cdev, devno, 1); if (ret) { printk(KERN_NOTICE "%s: Error %d dev %d.\n", DEV_NAME, ret, minor); } return ret;}/*設(shè)備驅(qū)動(dòng)模塊加載函數(shù)*/static int __init globalmem_init(void){ int ret = 0; dev_t devno; /* 申請(qǐng)?jiān)O(shè)備號(hào)*/ if(globalmem_major){ devno = MKDEV(globalmem_major, 0); ret = register_chrdev_region(devno, 2, DEV_NAME); }else{ /* 動(dòng)態(tài)申請(qǐng)?jiān)O(shè)備號(hào) */ ret = alloc_chrdev_region(&devno, 0, 2, DEV_NAME); globalmem_major = MAJOR(devno); } if (ret < 0) { return ret; } /* 動(dòng)態(tài)申請(qǐng)?jiān)O(shè)備結(jié)構(gòu)體的內(nèi)存,創(chuàng)建兩個(gè)設(shè)備 */ globalmem_devp = kmalloc(2*sizeof(globalmem_dev_t), GFP_KERNEL); if (!globalmem_devp) { unregister_chrdev_region(devno, 2); return - ENOMEM; } ret |= globalmem_setup(&globalmem_devp[0], 0); /* globalmem0 */ ret |= globalmem_setup(&globalmem_devp[1], 1); /* globalmem1 */ if(ret) return ret; init_MUTEX(&globalmem_devp[0].sem); /*初始化信號(hào)量*/ init_MUTEX(&globalmem_devp[1].sem); printk(KERN_INFO "globalmem: up %d,%d.\n", MAJOR(devno), MINOR(devno)); return 0;}/*模塊卸載函數(shù)*/static void __exit globalmem_exit(void){ cdev_del(&globalmem_devp[0].cdev); cdev_del(&globalmem_devp[1].cdev); kfree(globalmem_devp); unregister_chrdev_region(MKDEV(globalmem_major, 0), 2); printk(KERN_INFO "globalmem: down.\n");}/* 定義參數(shù) */module_param(globalmem_major, int, S_IRUGO);module_init(globalmem_init);module_exit(globalmem_exit);/* 模塊描述及聲明 */MODULE_AUTHOR("Archie Xie ");MODULE_LICENSE("Dual BSD/GPL");MODULE_DESCRIPTION("A char device module just for demo.");MODULE_ALIAS("cdev gmem");MODULE_VERSION("1.0");
用戶空間驗(yàn)證
切換到root用戶插入模塊
insmod globalmem.ko
創(chuàng)建設(shè)備節(jié)點(diǎn)(后續(xù)例程會(huì)展示自動(dòng)創(chuàng)建節(jié)點(diǎn)的方法)
cat /proc/devices 找到主設(shè)備號(hào)majormknod /dev/globalmem0 c major 0 和 /dev/globalmem1 c major 1
讀寫測(cè)試
echo "hello world" > /dev/globalmemcat /dev/globalmem
?
評(píng)論
查看更多