Linux设备驱动学习-first_drv.ko

1、linux内核模块简介node

linux内核总体结构很是庞大,其包含的组件也很是多。咱们怎么把须要的部分都包含在内核中呢?linux

     一种办法是把全部的须要的功能都编译到内核中。这会致使两个问题,一是生成的内核会很大,二是若是咱们要在现有的内核中新增或删除功能,不得不从新编译内核,工做效率会很是的低,同时若是编译的模块不是很完善,颇有可能会形成内核崩溃。算法

2、模块特色:函数

1)模块自己并不被编译入内核,从而控制了内核的大小。测试

 2)模块一旦被加载,他就和内核中的其余部分彻底同样。ui

    注意:模块并非驱动的必要形式:即:驱动不必定必须是模块,有些驱动是直接编译进内核的;同时模块也不全是驱动,例如咱们写的一些很小的算法能够做为模块编译进内核,但它并非驱动。就像烧饼不必定是圆的,圆的也不都是烧饼同样。this

3、韦东山老师的first_drv.ko分析spa

示例1日志

#include <linux/module.h>  /* __init __exit */ #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h>/* printk() */ #include <linux/delay.h> ... static int first_drv_open(struct inode *inode,struct file *file) { printk("first_drv_open\n"); return 0; } static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { printk("first_drv_write\n"); return 0; } static int  first_drv_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) { printk("first_drv_read\n"); return 0; } /* 这个结构是字符设备驱动程序的核心 * 当应用程序操做设备文件时所调用的open、read、write等函数, * 最终会调用这个结构中指定的对应函数 */
static struct file_operations first_drv_fops = { .owner = THIS_MODULE,/* 这是一个宏,推向编译模块时自动建立的__this_module变量 */ .open = first_drv_open, .read = first_drv_read, .write = first_drv_write, }; /*模块加载函数,经过insmod命令加载模块时,被自动执行*/
static int __init first_drv_init(void)//驱动入口函数
{ register_chrdev(111,"first_drv",&first_drv_fops);//注册一个主设备号为111的字符驱动设备 主设备号,设备名字,对应的结构体
    return 0; } /*模块卸载函数,当经过rmmod命令卸载时,会被自动执行*/
static void __exit first_drv_exit(void) { unregister_chrdev(111,"first_drv"); } /* 这两行指定驱动程序的初始化函数和卸载函数 */ module_init( first_drv_init); module_exit(first_drv_exit); MODULE_LICENSE("GPL");/*模块许可证实,描述内核模块的许可权限,必须*/

对应Makefilecode

KERN_DIR = /home/wang/linux-2.6.22.6// //内核路径,根据实际状况换成本身的内核路径,嵌入式的换成嵌入式,PC机的指定PC机路径
 all: make -C $(KERN_DIR) M=`pwd` modules clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order obj-m    += first_drv.o  //目标文件

最终会编译获得first_drv.ko文件,cp first_drv.ko "nfs文件系统" 使用insmod first_drv.ko加载模块,使用cat /proc/devices或dmesg可查看

经常使用的几种模块操做:

insmod XXX.ko    加载指定模块

lsmod                      列举当前系统中的全部模块

rmmod  XXX         卸载指定模块(注意没有.ko后缀)

dmesg                    当打印等级低于默认输出等级时,采用此命令查看系统日志

编写对应的应用测试程序

#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h>

int main(int argc, char **argv) { int fd; int val = 1; fd = open("/dev/xyz", O_RDWR); if (fd < 0) { printf("can't open!\n"); } write(fd, &val, 4); return 0; }

使用arm-linux-gcc -o firstdrvtest firstdrvtest.c 编译并拷贝到nfs文件系统

1建立设备节点 mknod /dev/xyz c 111 0 //建立主设备号为111,刺设备号0的字符设备/dev/xyz

2 执行测试应用程序 ./firstdrvtest

打印输出

first_drv_open

first_drv_write

上个驱动程序,须要本身指定主设备号,须要手动建立设备节点。那么是否能够自动建立?

1驱动:能够自动分配主设备号,也能够手动指定。

2应用 open("/dev/xxx") ->/dev/xxx怎么来?

  a.手动建立 mknod /dev/xxx c major minor。

  b自动建立 udev。在文件系统根目录下的sys目录,当注册一个驱动,会在该目录生成一个的信息,而mdev自动根据这些信息建立节点。

因此驱动程序须要提供设备信息以创建设备节点。

示例2

static struct class *firstdrv_class; static struct class_device    *firstdrv_class_dev; int major; static int _init first_drv_init(void)//驱动入口函数
{ major = register_chrdev(0,"first_drv",&first_drv_fops);//注册 返回的major就是自动分配的主设备号 firstdrv_class = class_create(THIS_MODULE, "firstdrv");//创建一个类 firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz");//类下边创建一个设备 次设备号为0 设备节点名为xyz
    return 0; } void first_drv_exit(void) { unregister_chrdev(major,"first_drv"); class_device_unregister(firstdrv_class_dev); class_destroy(firstdrv_class); }

mdev为何会根据这些信息建立设备节点?由于在etc/init.d/rcS文件中有

echo /sbin/mdev > /proc/sys/kernel/hotplug  

当有设备注册或卸载的时候,就会调用/proc/sys/kernel/hotplug

/** * class_device_create - creates a class device and registers it with sysfs * @cls: pointer to the struct class that this device should be registered to. * @parent: pointer to the parent struct class_device of this new device, if any. * @devt: the dev_t for the char device to be added. * @device: a pointer to a struct device that is assiociated with this class device. * @fmt: string for the class device's name * * This function can be used by char device classes. A struct * class_device will be created in sysfs, registered to the specified * class. * A "dev" file will be created, showing the dev_t for the device, if * the dev_t is not 0,0. * If a pointer to a parent struct class_device is passed in, the newly * created struct class_device will be a child of that device in sysfs. * The pointer to the struct class_device will be returned from the * call. Any further sysfs files that might be required can be created * using this pointer. * * Note: the struct class passed to this function must have previously * been created with a call to class_create(). */
struct class_device *class_device_create(struct class *cls, struct class_device *parent, dev_t devt, struct device *device, const char *fmt, ...)

示例3

一个类的设备中,有多个设备,好比3个led灯

static struct class *leds_class; static struct class_device    *leds_class_devs[3]; static unsigned long gpio_va; static int s3c24xx_leds_open(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); switch(minor) { case 0: { /*配置led1 gpio*/ } break; case 1: { /*配置led2 gpio*/ } break; case 2: { /*配置led3 gpio*/ } break; } return 0; } static ssize_t s3c24xx_leds_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { int minor = MINOR(file->f_dentry->d_inode->i_rdev); char val; copy_from_user(&val, buf, 1); switch(minor) { case 0: { /*根据val值控制led1 gpio*/ } break; case 1: { /*根据val值控制led2 gpio*/ } break; case 2: { /*根据val值控制led3 gpio*/ } break; } } static struct file_operations s3c24xx_leds_fops = { .owner =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动建立的__this_module变量 */ .open = s3c24xx_leds_open, .read = s3c24xx_leds_read, .write = s3c24xx_leds_write, }; /* * 执行insmod命令时就会调用这个函数 */
static int __init s3c24xx_leds_init(void) { int ret; int minor = 0; gpio_va = ioremap(0x56000000, 0x100000);//IO重映射 申请IM空间
    /* 注册字符设备 * 参数为主设备号、设备名字、file_operations结构; * 这样,主设备号就和具体的file_operations结构联系起来了, * 操做主设备为LED_MAJOR的设备文件时,就会调用s3c24xx_leds_fops中的相关成员函数 * LED_MAJOR能够设为0,表示由内核自动分配主设备号 */ ret = register_chrdev(231, “leds”, &s3c24xx_leds_fops); leds_class = class_create(THIS_MODULE, "leds"); for (minor = 0; minor < 3; minor++) { leds_class_devs[minor] = class_device_create(leds_class, NULL, MKDEV(LED_MAJOR, minor), NULL, "led%d", minor); } } /* * 执行rmmod命令时就会调用这个函数 */
static void __exit s3c24xx_leds_exit(void) { int minor; /* 卸载驱动程序 */ unregister_chrdev(LED_MAJOR, DEVICE_NAME); for (minor = 0; minor < 3; minor++) { class_device_unregister(leds_class_devs[minor]); } class_destroy(leds_class); iounmap(gpio_va); } /* 这两行指定驱动程序的初始化函数和卸载函数 */ module_init(s3c24xx_leds_init); module_exit(s3c24xx_leds_exit); MODULE_LICENSE("GPL");
相关文章
相关标签/搜索