源代码:
#include <linux/kernel.h> #include <linux/init.h> #include <linux/types.h> #include <linux/spinlock.h> #include <linux/blkdev.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/cdev.h> //module_param(dev_major, int, S_IRUGO); #define MY_DEV_MAJOR 260 /*预设的mem的主设备号*/ static int dev_major = 0; //MY_DEV_MAJOR; #define MY_DEV_NR_DEVS 2 /*设备数*/ #define CHAR_DEV_NAME "kpipe" /* 'k'为幻数,要按照Linux内核的约定方法为驱动程序选择ioctl编号, 应该首先看看include/asm/ioctl.h和Documentation/ioctl-number.txt这两个文件. 对幻数的编号千万不能重复定义,如ioctl-number.txt已经说明‘k'的编号已经被占用的范围为: 'k' 00-0F linux/spi/spidev.h conflict! 'k' 00-05 video/kyro.h conflict! 所以我们在这里分别编号为0x1a和0x1b */ #define CMD_MAGIC 'k' #define MEM_CMD1 _IO(CMD_MAGIC, 0x1a) #define MEM_CMD2 _IO(CMD_MAGIC, 0x1b) #define MEM_CMD3 _IO(CMD_MAGIC, 0x1c) #if 1 #define MAX_UID_NUM 32 typedef struct tagUSER_MSG_INFO { int uid; int param1; int param2; int param3; char *msg; }USER_MSG_INFO; typedef struct tagUSER_QUEUE_INFO { int rd_ptr; int wt_ptr; int max_msg; int msg_max_len; char *msg_queue; }USER_QUEUE_INFO; USER_QUEUE_INFO usr_queue[MAX_UID_NUM]; static int user_queue_init(void) { int i; for(i = 0; i < MAX_UID_NUM; i++) { memset(&usr_queue[i], 0, sizeof(USER_QUEUE_INFO)); } return 0; } static int user_queue_alloc(int uid, int max_msg, int msg_max_len) { int total_size; if (uid >= MAX_UID_NUM) return -1; if (max_msg < 1 || msg_max_len < 1) return -1; if (usr_queue[uid].msg_queue != NULL) return -2; total_size = max_msg * msg_max_len; usr_queue[uid].msg_queue = kmalloc(total_size, GFP_KERNEL); if (NULL == usr_queue[uid].msg_queue) return -3; usr_queue[uid].max_msg = max_msg; usr_queue[uid].msg_max_len = msg_max_len; usr_queue[uid].rd_ptr = 0; usr_queue[uid].wt_ptr = 0; return 0; } static int user_queue_push(USER_MSG_INFO *usr_msg) { int uid; char *ptr; if (NULL == usr_msg) return -1; uid = usr_msg->uid; if (usr_queue[uid].msg_queue == NULL) return -2; if ( (usr_queue[uid].wt_ptr + 1) % usr_queue[uid].max_msg == usr_queue[uid].rd_ptr) return -3; // full ptr = usr_queue[uid].msg_queue + usr_queue[uid].wt_ptr * usr_queue[uid].msg_max_len; memset(ptr, 0, usr_queue[uid].msg_max_len); copy_from_user((int *)ptr, (int *)usr_msg->msg, usr_queue[uid].msg_max_len); usr_queue[uid].wt_ptr = (usr_queue[uid].wt_ptr + 1) % usr_queue[uid].max_msg; return 0; } static int user_queue_pop(USER_MSG_INFO *usr_msg) { int uid; char *ptr; if (NULL == usr_msg) return -1; uid = usr_msg->uid; if (usr_queue[uid].msg_queue == NULL) return -2; if ( usr_queue[uid].rd_ptr == usr_queue[uid].wt_ptr) return -3; // empty ptr = usr_queue[uid].msg_queue + usr_queue[uid].rd_ptr * usr_queue[uid].msg_max_len; copy_to_user((int *)usr_msg->msg, (int *)ptr, usr_queue[uid].msg_max_len); usr_queue[uid].rd_ptr = (usr_queue[uid].rd_ptr + 1) % usr_queue[uid].max_msg; return 0; } static int user_queue_destroy(void) { int i; for(i = 0; i < MAX_UID_NUM; i++) { usr_queue[i].max_msg = 0; if (NULL != usr_queue[i].msg_queue) kfree(usr_queue[i].msg_queue); usr_queue[i].msg_queue = NULL; } return 0; } #endif #if 1 int my_dev_open(struct inode *inode, struct file *filp) { return 0; } int my_dev_release(struct inode *inode, struct file *filp) { return 0; } static int my_dev_ioctl( struct file *file, unsigned int cmd, unsigned long arg) { USER_MSG_INFO user_msg; int ret = 0; switch(cmd) { case MEM_CMD1: // init mq for uid if(copy_from_user(&user_msg, (int *)arg, sizeof(USER_MSG_INFO))) return -EFAULT; ret = user_queue_alloc(user_msg.uid, user_msg.param1, user_msg.param2); break; case MEM_CMD2: // send msg to uid if(copy_from_user(&user_msg, (int *)arg, sizeof(USER_MSG_INFO))) return -EFAULT; ret = user_queue_push(&user_msg); break; case MEM_CMD3: // get msg from uid if(copy_from_user(&user_msg, (int *)arg, sizeof(USER_MSG_INFO))) return -EFAULT; ret = user_queue_pop(&user_msg); break; } return ret; } static const struct file_operations my_dev_fops = { .owner = THIS_MODULE, .unlocked_ioctl = my_dev_ioctl, .open = my_dev_open, .release = my_dev_release, }; struct class *pclass = NULL; struct cdev my_dev; static int my_dev_init(void) { int result; dev_t devno = MKDEV(dev_major, 0); if (dev_major) { /* 静态申请设备号*/ result = register_chrdev_region(devno, 2, CHAR_DEV_NAME); } else { /* 动态分配设备号 */ result = alloc_chrdev_region(&devno, 0, 2, CHAR_DEV_NAME); dev_major = MAJOR(devno); } if (result < 0) return result; cdev_init(&my_dev, &my_dev_fops); my_dev.owner = THIS_MODULE; my_dev.ops = &my_dev_fops; cdev_add(&my_dev, MKDEV(dev_major, 0), MY_DEV_NR_DEVS); pclass = class_create(THIS_MODULE, CHAR_DEV_NAME); if (IS_ERR(pclass)) { printk("class_create failed!/n"); goto failed; } user_queue_init(); device_create(pclass, NULL, devno, NULL, CHAR_DEV_NAME); return 0; failed: cdev_del(&my_dev); unregister_chrdev_region(devno, 1); return result; } static void my_dev_exit(void) { device_destroy(pclass, MKDEV(dev_major, 0)); class_destroy(pclass); cdev_del(&my_dev); unregister_chrdev_region(MKDEV(dev_major, 0), 2); user_queue_destroy(); } #endif MODULE_AUTHOR("derek.yi"); MODULE_LICENSE("GPL"); module_init(my_dev_init); module_exit(my_dev_exit);
测试代码:
#include <stdio.h> #include <fcntl.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <sys/ioctl.h> #define CMD_MAGIC 'k' #define MEM_CMD1 _IO(CMD_MAGIC, 0x1a) // init #define MEM_CMD2 _IO(CMD_MAGIC, 0x1b) // write #define MEM_CMD3 _IO(CMD_MAGIC, 0x1c) // read typedef struct tagUSER_MSG_INFO { int uid; int param1; int param2; int param3; char *msg; }USER_MSG_INFO; #define _APP_001_ #ifdef _APP_001_ int main() { int fd; int ret; char buff[256]; USER_MSG_INFO usr_msg; fd = open("/dev/kpipe", O_RDWR); if( fd < 0 ) { printf("open kpipe WRONG!\n"); return 0; } usr_msg.uid = 0; usr_msg.param1 = 64; // max msg usr_msg.param2 = 128; // max len ret = ioctl(fd, MEM_CMD1, &usr_msg); printf("ioctl: ret=%d\n", ret); usr_msg.msg = &buff[0]; sprintf(buff, "hello,pipe\n"); ret = ioctl(fd, MEM_CMD2, &usr_msg); printf("ioctl: ret=%d\n", ret); usr_msg.msg = &buff[0]; memset(buff, 0, 256); ret = ioctl(fd, MEM_CMD3, &usr_msg); printf("ioctl: ret=%d rdata=%s\n", ret, buff); close(fd); return 0; } #endif