Note:仅适合初学者~~~~~
环境:linux 3.8内核
- 编写驱动程序,命名为demo.c
#include <linux/fs.h> #include <linux/cdev.h> #include <linux/uaccess.h> #include <linux/parport.h> #include <linux/module.h> MODULE_LICENSE("Dual BSD/GPL"); #define DEVICE_NAME "demo" #define BUF_MAX_LEN 50 char buf[BUF_MAX_LEN]; static dev_t dev_number; static struct cdev demo_cdev; ssize_t demo_read (struct file * file , char __user* buffer, size_t count, loff_t * ppos); int demo_release (struct inode * inode, struct file * file); static int demo_open (struct inode * inode, struct file * file); ssize_t demo_write(struct file * file, const char __user* buffer, size_t count,loff_t * ppos); int demo_init(void); void demo_clean (void); static struct file_operations __demo_fops = { .owner = THIS_MODULE, .open = demo_open, .write = demo_write, .release = demo_release, .read = demo_read, }; static int demo_open (struct inode * inode, struct file * file) { sprintf (buf, "device open successfully!\n"); printk ("device open successfully!\n"); //printk:在内核中运行的向控制台输出的函数 return 0; } ssize_t demo_write(struct file * file, const char __user* buffer, size_t count,loff_t * ppos) { if (count > BUF_MAX_LEN) count = BUF_MAX_LEN; if (copy_from_user (buf, buffer, count)) return -EFAULT; printk ("user write data to driver\n"); return count; } ssize_t demo_read (struct file * file , char __user* buffer, size_t count, loff_t * ppos) { if (count > BUF_MAX_LEN) count = BUF_MAX_LEN; if (copy_to_user (buffer, buf, count)) return -EFAULT; //copy_to_user:从内核空间拷贝数据到用户空间 printk ("user read data from driver\n"); return count; } int demo_release (struct inode * inode, struct file * file) { return 0; } int demo_init(void) { if (alloc_chrdev_region (&dev_number, 0, 1, DEVICE_NAME) < 0) { printk("Can not register!\n"); return -1; } cdev_init(&demo_cdev, &__demo_fops); demo_cdev.owner = THIS_MODULE; if (cdev_add (&demo_cdev, dev_number, 1)) { printk("Bad cdev add\n"); return 1; } printk("Initialized successfully!"); return 0; } void demo_clean (void) { printk("Demo exit!"); unregister_chrdev_region(dev_number, 1); return ; } module_init(demo_init); module_exit(demo_clean);
程序不作解释,哪里不懂的谷歌百度或者直接看看源代码就有很多相关资料
- 在存放demo.c的目录下,添加命名为Makefile的文件,网上有人说命名为makefile可能会出错,此处命名为Makefile.
ifneq ($(KERNELRELEASE),) obj-m :=demo.o else KDIR := /lib/modules/$(shell uname -r)/build all: make -C $(KDIR) M=$(PWD) modules clean: rm -f *.ko *.o *.mod *.mod.c *sysmvers endif
- 终端进入存放demo.c和Makefile的目录,执行命令"sudo make",系统会自动找到目录下的Makefile文件.
- make成功后可以动态加载demo模块了,命令: sudoinsmod demo.ko
- 使用命令"lsmod"列出模块,应当可以在"Module"一列看到demo,这表明加载成功.
- 接下来可以使用mknod生成节点,但在此之前要先获得系统分配给demo的主设备号,使用"cat /proc/devices"命令.由于demo是字符设备,故在Character devices中可以找到demo,假设查得主设备号是250
- 现在可以执行mknod了,如何使用?执行命令"mknod --help"查看帮助,看到用法:mknod [选项]... 名称 类型 [主设备号 次设备号],名称设为/dev/demo(/dev是系统中默认已存在的目录,执行mknod命令后在/dev中会生成demo),类型是chrdev(字符设备),主设备号上一步中查得为250,次设备号这里我们设为0就好.于是我们输入命令"sudo mknod /dev/demo chrdev 250 0"
- 使用"cd /dev"命令进入/dev,输入命令"find -name demo",如果输出"./demo"或类似的结果就代表mknod成功!
- 现在可以尝试调用我们的驱动了,测试程序test.c:
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> int main() { int fd = -1; int i; char buf[255]; fd = open ("/dev/demo", O_RDWR); if (fd < 0) { printf ("### DEMO device open fail ###\n"); return (-1); } read (fd, buf, 40); printf("First read data: %s", buf); printf("Please input string\n"); scanf("%s", buf); write(fd, buf, 40); read(fd, buf, 40); printf("Second read data: %s\n", buf); close(fd); return 0; }
终端进入存放test.c的目录,使用命令"gcc test.c -o test"生成可执行文件,再输入命令"sudo ./test"执行程序,效果:First read data: device open successfully!
Please input string
1
Second read data: 1 - 此时可以查看系统记录,输入命令"sudo dmesg | tail",这些记录是demo.c中使用printk函数输出的:[ 2535.104992] device open successfully!
[ 2535.105008] user read data from driver
[ 2544.812868] user write data to driver
[ 2544.812888] user read data from driver
>>完