Linux设备驱动基础
1.2 设备驱动基础
驱动基本结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// hello_drv.c #include <linux/init.h> #include <linux/module.h> static int __init hello_drv_init(void) { printk(KERN_ERR"-----------from hello.ko : %s %s---------\n", __FILE__, __FUNCTION__); return 0; } static void __exit hello_drv_exit(void) { printk("-----------from hello.ko : %s %s---------\n", __FILE__, __FUNCTION__); } module_init(hello_drv_init); module_exit(hello_drv_exit); MODULE_LICENSE("GPL");
基本
Makefile
结构运行/装载驱动模块
其他特性
模块传参
模块符号导出
EXPORT_SYMBOL(符号名)
- 装载的时候先装载被调用的模块
一个真正的设备驱动需要一些元素
设备号
1 2 3 4 5 6 7 8
register_chrdev(unsigned int major, const char * name , const struct file_operations * fops); /* * 参数1 --- 指定一个主设备号,填0表示由系统自动分配 * 参数2 --- 字符串, 描述设备驱动信息,自定义 * 参数3 --- 文件操作对象 * 如果参数1为0,则返回值是一个int型的主设备号,否则返回负数为错误号, generic */ unregister_chrdev(unsigned int major, const char * name) //--- 释放已经注册的主设备号
设备文件
手动创建: 每次重启都得手动创建
mknod /dev/hello c 254 0
自动创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#include <linux/device.h> struct class * class_create(THIS_MODULE, “led_cls”) /* * 参数一: THIS_MODULE * 参数二 : 字符串,表示类的名字 * 返回值 struct class 指针类型 */ struct device * device_create(struct class * cls, struct device * parent, dev_t devt, void * drvdata, const char * fmt, ...) /* * 参数一: class_create()返回的指针 * 参数二: 该设备的父类 * 参数三: 设备号, 包含主设备号和次设备号,MKDEV(major, minor) * 参数四: 私有数据指针,可以为NULL * 参数五: 设备节点名字 * 返回值: struct device * */ void device_destory(struct device * dev, dev_t devt); void class_destory(struct class * cls);
设备的操作方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
/* * NOTE: * all file operations except setlease can be called without * the big kernel lock held in all filesystems. */ struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); unsigned int (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); /*省略其他不常用的*/ }; /* * 设备操作命令如何定义: 由程序员定义,一定是一个整数 * 1. 直接用整数 --- 有可能会和系统中已经有的命令冲突 * #define LED_ALL_ON 0x2222 * 2. 用内核提供的接口来定义 #include <asm/ioctl.h> _IO(type, nr) //参数一 :魔幻数---字符,参数二:唯一的数字 __IOW(type, nr, size) __IOR(type, nr, size) __IOWR(type, nr, size) */ /* 原型如下 */ #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) #define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size)) #define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size)) #define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size)) /* ------------------ 例 ----------------*/ #define LED_NUM_ON _IOW('L', 0x3456, int) #define LED_NUM_OFF _IOW('L', 0x3457, int) #define LED_ALL_ON _IOW('L', 0x3458) xxx_ioctl(int fd, int cmd, unsigned long args) { int num = args; switch(cmd) { case LED_NUM_ON: xxx break; case LED_NUM_OFF: xxx break; case LED_ALL_ON: xxx break; } }
内存映射与映射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
#include <linux/slab.h> /** * kzalloc - allocate memory. The memory is set to zero. * @size: how many bytes of memory are required. * @flags: the type of memory to allocate (see kmalloc). */ static inline void *kzalloc(size_t size, gfp_t flags) //flags: GFP_KERNEL 如果当前暂时没有内存,会尝试等待(阻塞) kfree() #include <asm/io.h> #define ioremap(cookie, size) __uc32_ioremap(cookie, size) /* * 参数一:物理地址 * 参数二:映射的长度 * 返回值:映射之后的虚拟地址 */ /* ------------------ 例 ----------------*/ gpc0_conf = ioremap(0xE020060, 8); gpc0_data = gpc0_conf+1; //配置输出 *gpc0_conf &= ~(0xff<<12); //清零 *gpc0_cong |= (0x11<<12); //置1 //亮灯 *gpc0_data |= 0x3<<3; //灭灯 *gpc0_data &= ~(0x3<<3);
数据在内核空间和用户空间拷贝的方法