基于s5pv210处理器驱动相关整理

驱动


===========================================================

一、驱动模块 基本框架

相关头文件:

#include<linux/init.h>

#include<linux/module.h>

(一)函数实现:

1、入口函数

static int  __init hellokernel_init(void)

2、出口函数

static void  __exit hellokernel_exit(void)

(二)模块声明:

module_init(hellokernel_init);

module_exit(hellokernel_exit);

(三)/*添加模块的许可声明*/

MODULE_LICENSE("GPL");//必须加

===========================================================

二、GPIO 操作相关函数

相关头文件:

#include<asm/gpio.h>

#include<plat/gpio-cfg.h>

(一)函数实现:

1、申请GPIO资源

  int gpio_request(unsigned gpio,char* name);

  参数:gpio:GPIO编号:

                 S5PV210_GPC0(3)

                 S5PV210_GPC1(3)

            name:标识

  返回值:成功返回0,失败返回负值

2 、释放已经申请的GPIO资源  

  void gpio_free(unsigned gpio)

  参数:gpio:GPIO编号

3 、设置GPIO的为输入口

  int gpio_direction_input(unsigned gpio);

  参数:gpio:GPIO编号

4 、设置GPIO为输出口

  int gpio_direction_output(unsigned gpio,intvalue);

  参数:gpio:GPIO编号

        value: 对应GPIO默认输出的值

5 、设置GPIO的输出状态

  void gpio_set_value(unsigned gpio,int value)

  参数:gpio:GPIO编号

        value: 对应GPIO输出的值 0/1

6 、读取GPIO状态

  int gpio_get_value(unsigned gpio);

  参数:gpio:GPIO编号

  返回值:当前引脚的状态信息 0/1

===========================================================

三、字符设备 驱动编写流程之(手动创建设备节点)
相关头文件:

#include<linux/fs.h>  //structfile_operations

#include<linux/cdev.h>    //struct cdev

1、定义 申请 设备号

static dev_t dev;    //定义设备号

alloc_chrdev_region(&dev,0,1,"led_cdev"); //向内核申请设备号

2、定义 初始化 硬件操作方法对象
struct  file_operations  led_fops = {
           .open = led_open,
        .release = led_close,
        .read = led_read,
        .write = led_write
};
3、定义初始化 字符设备对象
struct cdev led_cdev; //定义对象

cdev_init(&led_cdev,&led_fops);             //初始化字符设备对象
结果: led_cdev.ops = &led_fops
初始化字符设备对象,给字符设备对象添加硬件操作方法
4、注册字符设备驱动到内核
cdev_add(&led_cdev, 申请的设备号, 次设备号的个数);
至此: 一旦安装完毕,内核就有一个真实的字符设备驱动,并且这个驱动给用户提供了相关的操作方法(open,close,read,write)
5、编写以上四个接口函数, 这四个接口里面就是对硬件操作的细节
6、字符设备不再使用时,要卸载:
   1、卸载字符设备驱动
   cdev_del(&led_cdev);
   2、释放设备号

unregister_chrdev_region(dev,1);            //释放设备号

===========================================================

四、字符设备 驱动编写流程之(自动创建设备节点)

相关头文件:

#include <linux/device.h>     //设备文件自动创建

(一)创建设备类(长树枝)

   struct class* cdev_class;    //定义设备类指针

   cdev_class =class_create(THIS_MODULE,"name");
例:cls= class_create(THIS_MODULE, "tarena");

   第一个参数类的所有者模块,一般传:THIS_MODULE

   第二个参数时类目录名,在/sys/class下创建类目录 

(二)创建设备文件(长苹果)

例:device_create(cls,NULL, dev, NULL, "myled");

   struct device *device_create(

                 structclass *cls,

                 struct device *parent,

                 dev_tdevt, void *drvdata,

              const char *fmt, ...)    

   @cls:struct class指针

   @parent:该设备的parent指针,一般为NULL

   @devt:字符设备的设备号   

   @drvdata:被添加到该设备回调的数据,一般为NULL

   @“const char *fmt, ...”  :设备名字,对应设备文件名

          eg:“myled”      ---> /dev/myled

          eg:“myled1”     ---> /dev/myled1 

(三)删除设备文件

   device_destroy(cdev_class,devt);

(四)删除设备类

   class_destroy(cdev_class);

(五)设备号操作相关的宏函数

  设备号 = MKDEV(主设备号,次设备号)

  主设备号 = MAJOR(设备号)

  次设备号 = MINOR(设备号)

===========================================================

五、linux字符设备驱动硬件操作接口--read

(一)应用程序read系统调用

   char buf[1024]={0};

   int ret = read(fd,buf,1024);

   从fd对应设备读取数据,存放到用户buf缓冲区,要读1024个字节

   ret表示实际读到的字节数

 

(二)对应的底层驱动操作接口函数

structfile_operations {

   ssize_t (*read)(struct file *file, char__user *buff,

                               size_tcount, loff_t *off);

}

@file:文件指针,对应fd

@buff:用户缓冲区的首地址,内核代码不能直接访问,如果需要将内核缓冲区的数据拷贝到用户缓冲区使用,需要使用内核函数:

       copy_to_user()。

@count: 要拷贝字节个数

@off:偏移,记录上一次的读写位置。

    一般用于多次读操作:

    eg:

    loff_t pos = *off;//读到了100字节

    *off = pos + 100;

 

相关头文件:

#include<asm/uaccess.h>     //copy_from_user

(三)内核函数copy_to_user

   int copy_to_user(void __user *to,const void*from,

                                      unsignedlong n);

   函数功能:将内核缓冲区的数据拷贝到用户缓冲区

   @to:目标地址,保存的是用户缓冲区的首地址

   @from:源地址,保存内核缓冲区的首地址

   @n:拷贝字节数

   返回值:成功返回0,失败返回非0(没有拷贝成功后剩余的字节数)

 

案例:读取灯的状态

0:LED1、LED2都关

1:LED1开LEED2关

2:LED1关LED2开

3:LED1、LED2都开 

===========================================================

六、linux字符设备操作硬件接口--write

(一)应用程序write系统调用

   char buf[1024] = "hello world!";

   write(fd,buf,strlen(buf));

 

(二)对应底层系统调用接口

   ssize_t (*write)(struct file *file, constchar __user *buf,

                               size_tcount, loff_t *off);

   @file:文件指针

   @buf:用户缓冲区的首地址

   @count:要写入的字节个数

   @off:记录上一次读写位置                          

 

相关头文件:

#include<asm/uaccess.h>     //copy_from_user

(三)内核函数copy_from_user

   copy_from_user(void* to,const void __user*from,

                 unsignedlong n);

   函数功能:将用户缓冲区的数据拷贝到内核缓冲区

   @to:目标地址,内核缓冲区的首地址

   @from:源地址,用户缓冲区的首地址

   @n:拷贝的字节数

   返回值:成功返回0,失败返回非0(拷贝剩余的字节数);

案例:

   用户进程写1,打开LED1和LED2

   用户进程写0,关闭LED1和LED2

  

练习:用户能够控制某个灯的开关 

   struct led_cmd{

          intcmd;//控制开关

          intindex;//灯的编号

   };

   struct led_cmd cmd;

   write(fd,&cmd,sizeof(cmd));

===========================================================

七、linux字符设备驱动硬件接口--ioctl

(一)应用程序中ioctl系统调用

   int ioctl(int fd, int cmd,...);

   函数功能:向设备发送控制命令,能够和设备读/写操作

   @fd:设备文件描述符

   @cmd:向设备发送的控制命令,命令由程序员自己单独的定义

   @...:表示可选参数,一般传递用户缓冲区的首地址,驱动程

         序中通过copy_to/from_user进行数据交互。

        

   案例:通过ioctl控制led开关,应用程序:

   #define LED_ON  0x100001

   #define LED_OFF  0x100002

   int fd=open("/dev/myled",...)

   //仅仅 开/关 灯

   ioctl(fd,LED_ON);

   ioctl(fd,LED_OFF);

   //指定开/关哪一个灯

   int index = 1;

   ioctl(fd,LED_ON,&index);

   ioctl(fd,LED_OFF,&index);

 

(二)对应底层驱动的ioctl接口

   long (*unlocked_ioctl) (struct file *file,

          unsignedint cmd, unsigned long arg);

   参数:

   @file:和前面一样,对应文件描述符

   @cmd:保存用户向设备发送的控制命令

          switch(cmd){

          caseLED_ON:

                 开灯操作

                 break;

          caseLED_OFF:

                 关灯操作

                 break;

          }

   @arg:保存用户缓冲区的首地址,内核程序不能直接使用  

       

   案例:通过ioctl控制某个指定led开/关,驱动程序实现

===========================================================

八、混杂设备驱动(misc)--编写步骤

相关头文件:

#include<linux/device.h>      //设备文件自动创建

#include<linux/miscdevice.h>      //structmiscdevice

(一)定义初始化混杂设备对应的硬件操作方法

static structfile_operations led_fops = {

.owner = THIS_MODULE,

.unlocked_ioctl = led_ioctl     // 发送命令/ 读/ 写

};

(二)定义初始化混杂设备对象

struct miscdeviceled_misc = {

.minor = MISC_DYNAMIC_MINOR,

.name = "myled",

.fops = &led_fops

};

(三)安装混杂设备对象到内核

misc_register(&led_misc);

(四)卸载混杂设备对象

misc_deregister(&led_misc);

//切记:本质上还是字符设备驱动

===========================================================

九、linux内核中断编程

相关头文件:

#include<linux/irq.h>

#include<linux/interrupt.h>         //中断相关头文件

 

#include<linux/input.h>         //标准按键值

(一)注册中断

   int request_irq(unsigned int irq,irq_handler_t handler,

          unsignedlong flags,const char *name, void *dev)

   函数功能:向内核申请硬件中断资源,然后注册硬件中断的

             中断处理函数到内核。

   参数:

   @irq:硬件中断对应的软件编号,称为中断号

          //linux/arch/arm/plat-s5p/include/plat/irqs.h

          //linux/arch/arm/mach-s5pv210/include/mach/irqs.h

       eg:按键“UP”中断号

       查看硬件电路原理图-->硬件中断XEINT0

       对应的软件中断编号-->IRQ_EINT(0)

   @handler:中断处理函数(中断处理例程/中断服务程序) 

       Typedef  irqreturn_t (*irq_handler_t)(int//中断号, void *//参数);

       typedef  enum irqreturn  irqreturn_t;

       enum irqreturn {

         IRQ_NONE,//中断不做处理,异常

         IRQ_HANDLED,//中断被处理,正常

         IRQ_WAKE_THREAD,

       };

   @flags:中断标志,中断触发方式

          IRQ_TRIGGER_FALLING:下降沿

          IRQ_TRIGGER_RISING:上升沿

          IRQ_TRIGGER_HIHG:高电平

          IRQ_TRIGGER_LOW:低电平

          多个标志,可以做位运算

          eg:按键按下和抬起都产生中断

          IRQ_TRIGGER_FALLING| IRQ_TRIGGER_RISING

   @name:中断名称

   @dev:给中断处理函数传递的参数   

 

   返回值:成功返回0,失败返回负值:-EINVAL、-EBUSY

  

(二)释放中断

   void free_irq(int irq,void* dev)

   @irq:中断号

   @dev:中断处理函数的参数     //和注册时候传递的参数一致



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

a746742897

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值