博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
创建一个字符设备1.2
阅读量:7291 次
发布时间:2019-06-30

本文共 4635 字,大约阅读时间需要 15 分钟。

  

          本文接着《创建一个字符设备1.1》讲如何创建字符设备模型、怎么创建设备文件

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

    创建字符设备流程

       1.定义一个cdev

  2.申请设备号
    .静态注册
    .MKDEV
    .register_chrdev_region

    .动态注册

    .alloc_chrdev_region

  3.定义file_operations,并且初始化

  4.cdev初始化

    .cdev_init

  5.将cdev加入到内核

    .cdev_add

  6.创建设备文件
    .手动创建设备文件,去/dev目录进行创建
    .自动创建

 

  前面《创建一个字符设备1.1》已经讲了初始化驱动函数、卸载驱动函数。

这里多了设备、设备号(主次设备号)、为应用层提供操作设备驱动的方法集、设备文件。

------------------------------------------------------------------------------------------------------------

    针对上面的内容,下面会一一讲解。

  1.我们先看设备文件,在/dev 目录下可查看当前挂载的设备文件

  [root@Mr.Jin Fa /dev]# ls -l

  c: 字符设备                10主设备号   242次设备号          CEC设备文件名

  crw-rw---- 1 root root 10,     242 Jan 1 16:04     CEC
  crw-rw---- 1 root root 10,     243 Jan 1 16:04     HPD
  crw-rw---- 1 root root 14,      12 Jan 1 16:04      adsp

设备文件是驱动层用来给应用层调用(调用应用层调用open()获取文件描述符时)

 

  2.设备是一个由硬件抽象出来的“实体”,它具有完成驱动程序功能。用lsmod查看当前挂载的设备

[root@Mr.Jin Fa /]# lsmod

rtnet3070ap   24124 0   -   Live   0xbf0d3000
rt3070ap     488261   1   rtnet3070ap, Live 0xbf044000 (P)
led_drv     1203 0   -     Live     0xbf028000
buzzer_drv    1488 0   -     Live      0xbf022000

........

    3.设备号是作为区分驱动的标识符,它如进程的PID。

主设备号:用来区分不同硬件类型,如网络设备和串口设备它们的主设备号很大程度是不同的

(为什么这样说呢,因为还是一个原则保证设备号不同就好了)。

次设备号:用来区分同一硬件类型下的不同硬件,如串口0和串口1硬件类型相同,它们的主设备号

相同,但是次设备号不会相同。

设备号:通过调用MKDEV(adc_major,adc_minor)函数输入想要申请的主设备号、次设备号。返回设备号

 

  4.用于层操作驱动方法集和

  static const struct file_operations gec210_led_fops = {

  .owner = THIS_MODULE,        申明这个驱动属于本内核
  .write  = gec210_led_write,      定义用作写函数
  .open = gec210_led_open,  定义打开驱动函数
  .release = gec210_led_close,   定义用于关闭驱动函数
};

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

        下面贴代码细讲

1 #include 
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 10 static struct cdev gec210_led_drv; // 定义一个设备 11 unsigned int major=238,minor=0; // 定义并初始化表示主次设备号的变量 12 unsigned int led_num=0;       // 定义设备号 13 int rt=0;              // 定义表示错误返回码 14 15 static struct resource *led_res; // ioremap()后的虚拟地址的首地址 16 static void __iomem * gpj2con_va;   // 表示gpj2con_va虚拟地址变量 17 static void __iomem * gpj2dat_va;   // 表示gpj2dat_va 虚拟地址变量 18 19 20 int gec210_led_open (struct inode * inode, struct file *file) 21 { 22 printk("gec210_led_open\n"); 23 //将GPJ2CON配置为输出模式 24 *(unsigned int *)gpj2con_va &=~0xFFFF; 25 *(unsigned int *)gpj2con_va |= 0x1111; 26 *(unsigned int *)gpj2dat_va |= 0xF; 27 28 return 0; 29 } 30 31 32 int gec210_led_close (struct inode *inode, struct file *file) 33 { 34 printk("gec210_led_close\n"); 35 return 0; 36 37 } 38 39 ssize_t gec210_led_write (struct file *file, const char __user *buf, size_t len, loff_t *offset) 40 { 41 char kbuf[2]={ 0}; 42 if(len > 2) 43 return -EINVAL; 44 rt = copy_from_user(kbuf,buf,len); 45 if(rt !=0){ 46 printk("copy_from_user fail\n"); 47 return -EFAULT; 48 } 49 if(kbuf[1]) 50 { 51 *(unsigned int *)gpj2dat_va &=~(1<

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

            应用层代码

1 #include 
2 #include
3 4 int main(void) 5 { 6 int fd; 7 int ret; 8 char led0_on_buf[2]={
0,1}; //0,led的号码;1,亮 9 char led0_off_buf[2]={
0,0}; //0,led的号码;0,灭 10 fd = open("/dev/led_drv", O_RDWR);11 if(fd < 0){12 perror("open /dev/");13 return -1; 14 } 15 while(1)16 {17 //亮18 write(fd,led0_on_buf,2);19 sleep(1); 20 21 //灭22 write(fd,led0_off_buf,2);23 sleep(1); 24 }25 close(fd); 26 return 0;27 }

  由于我们没有做杂项设备、类设备所以我们必须在/dev 目录下新建设备文件

先看加载驱动 insmod 

[root@Mr.Jin Fa /]# insmod LedDrv.ko

[ 4480.789028] hello gec210 led driver,gpj2con_va=e09de280,gpj2dat_va=e09de284

在/dev 目录下建设备文件

[root@Mr.Jin Fa /]# insmod /dev/led_drv c 250 0

[root@Mr.Jin Fa /dev]# ls -l | grep "led_drv"

crw-r--r-- 1 root root 250, 0 Jan 12 19:13 led_drv

     然后我们之间运行程序就好了, 下面是效果:

[root@GEC210 /]# chmod 777 led_test

[root@GEC210 /]# ./led_test
[ 4835.601157] gec210_led_open
[ 4835.601205] gec210_led_write,kbuf[0]=0,kbuf[1]=1
[ 4836.601335] gec210_led_write,kbuf[0]=0,kbuf[1]=0
[ 4837.601422] gec210_led_write,kbuf[0]=0,kbuf[1]=1
[ 4838.601510] gec210_led_write,kbuf[0]=0,kbuf[1]=0
[ 4839.601599] gec210_led_write,kbuf[0]=0,kbuf[1]=1
[ 4840.601686] gec210_led_write,kbuf[0]=0,kbuf[1]=0
^C[ 4841.497062] gec210_led_close

 

转载于:https://www.cnblogs.com/huang321/p/6868214.html

你可能感兴趣的文章
Easyui Datagrid 如何实现后台交互显示用户数据列表
查看>>
模块登录页代码
查看>>
CCF-CSP 201709-3 JSON查询 题解
查看>>
获取当前路径 ${pageContext.request.contextPath}
查看>>
开博始点,凡程子来了
查看>>
[问题记录]Ubuntu下chmsee安装失败的解决
查看>>
iOS文档注释
查看>>
工厂模式
查看>>
Revit API根据参数类型取得参数的值
查看>>
python18天-pycharm & 正则表达式
查看>>
pdf转txt
查看>>
牛客假日团队赛1 D.Promotion Counting
查看>>
php jsonp 跨域问题 js端
查看>>
点击头像单独把图片拉取出来.然后再次点击回到初始效果
查看>>
7-2 朋友圈 (25 分)
查看>>
转:手机流畅的决定性因素
查看>>
nginx离线部署脚本
查看>>
移动端多行文本溢出省略
查看>>
React Native 项目实战 -- DoubanProject
查看>>
JS 获取form表单的所有数据
查看>>