嵌入式驱动day05

1.字符设备驱动框架
struct cdev {//代表硬件上的一个字符设备
    dev_t dev;//设备号
    struct file_operations f_ops;//操作函数集合
    ....
}

关于设备号的分配方式
    静态分配
        register_chrdev_region
    动态分配
        alloc_chrdev_region
 关于cdev的操作
        cdev_init
        cdev_add
        cdev_del
 struct file_operations{
        open
        read
        write
        release
   }   

    struct    inode{
        dev_t    i_rdev;//记录设备号
        struct cdev *i_cdev;//关于cdev结构
   }

    struct    file{
        void    *private_data;
     }

    fd0=open("/dev/cdd0")    struct file
    fd1=open("/dev/cdd1")    struct file
    read(fd0,..)    ->    cdd_read(struct file*    filp,..)
    read(fd1,..)    ->    cdd_read(struct file*    filp,..)
------------------------------------------------------------------
用户空间和内核空间数据交换的问题
    copy_to_user
    copy_from_user
    day04代码多次读返回同样的内容
    添加    llseek    函数
  1. #include<linux/init.h>
  2. #include<linux/module.h>
  3. #include<linux/fs.h>
  4. #include<linux/cdev.h>
  5. #inlcude<linux/device.h>
  6. #include<asm/uaccess.h>//用户空间拷贝头文件
  7. //动态申请的头文件
  8. #include<linux/slab.h>
     #include<liunx/string.h>
  1. MODULE_LICENSE("GPL");
  2. #define CDD_MAJOR 200
  3. #define CDD_MINOR 0
  4. #define CDD_COUNT 10
  5. dev_t dev=0;
  6. u32 cdd_major=0;
  7. u32 cdd_minor=0;
  8. /*定义cdev类型的变量*/
  9. struct cdev cdd_cdev;
  10. struct class *dev_class=NULL;
  11. //struct device *dev_device=NULL;
  12. #define BUF_SIZE 100
  13. struct cdd_cdev{
  14. struct cdev cdev;
  15. struct device *dev_device;
  16. u8 led;
  17. char kbuf[BUF_SIZE];
  18. u32 data_len;//记录缓冲区已写入数据的长度
  19. }
  20. struct cdd_cdev *cdd_cdevp=NULL;
  21. int cdd_open(struct inode *inode,struct file *filp){
  22. /*2*/
  23. printk("enter cdd_open\n");
  24. struct cdd_cdev* pcdevp=NULL;
  25. printk("enter cdd_open\n");
  26. pcdevp = container_of(inode->i_cdev,struct cdd_cdev,cdev);
  27. printk("led=%d\n",pcdevp->led);
  28. filp->private_data = pcdevp;
  29. return 0;
  30. }
  31. int cdd_read(struct file* filp,char __user *buf,size_t count,
  32. loff_t *offset){
  33. int ret=0;
  34. u32 pos=*offset;
  35. u32 cnt=count;
  36. struct cdd_cdev* pcdevp = filp->private_data;
  37. printk("LED%d\n",pcdevp->led);
  38. //copy_to_user(to,from,n);
  39. //先判断下有没有数据可以进行读取
  40. if(cnt>(pcdevp->data_len/*先有数据*/ - pos/**/)){
  41. cnt=pcdevp->data_len-pos;
  42. }
  43. ret=copy_to_user(buf,pcdevp->kbuf+pos,cnt);
  44. *offset += cnt;
  45. return ret;
  46. }
  47. int cdd_write(struct file *filp,const char __user *buf,
  48. size_t count,loff_t *offset){
  49. printk("enter cdd_write!\n");
  50. int ret=0;
  51. u32 pos=*offset;
  52. u32 cnt =count;
  53. if(cnt > (BUF_SIZE - pos)){
  54. cnt = BUF_SIZE - pos;
  55. }
  56. copy_from_user(pcdevp->kbuf+pos,buf,cnt);
  57. *offset += cnt;
  58. if(*offset > pcdevp->data_len){
  59. pcdevp->data_len = *offset
  60. }
  61. return ret;
  62. }
  63. int cdd_ioctl(struct inode *inode,struct file *filp,
  64. unsigned int cmd ,unsigned data){
  65. printk("enter cdd_ioctl!\n");
  66. return 0;
  67. }
  68. int cdd_release(struct inode *inode,struct file *filp){
  69. printk("enter cdd_release!\n");
  70. return 0;
  71. }
  72. loff_t cdd_llseek(struct file *filp,loff_t offset,int whence){
  73. struct cdd_cdev *pcdevp=filp->private_data;
  74. loff_t newpos=0l
  75. switch(whence){
  76. case SEEK_SET:
  77. newpos=offset;
  78. break;
  79. case SEEK_CUR:
  80. newpos=filp->fops+offset;
  81. break;
  82. case SEEK_END:
  83. newpos=pcdevp->data_len+offset;
  84. break;
  85. default:
  86. return -EINVAL;
  87. }
  88. if(newpos<0 || newpos>=BUF_SIZE){
  89. return -EINVAL;
  90. }
  91. filp->f_ops=newpos;
  92. return newpos;
  93. }
  94. struct file_operations cdd_fops={
  95. .owner = THIS_MODULE,
  96. .open=cdd_open,
  97. .read=cdd_read,
  98. .write=cdd_write,
  99. .ioctl=cdd_ioctl,
  100. .release=cdd_release,
  101. .llseek=cdd_llseek,
  102. };
  103. int __init cdd_init(void){
  104. int ret =0;
  105. int i;
  106. if(cdd_major){
  107. //静态方式分配设备号
  108. dev=MKDEV(CDD_MAJOR,CDD_MINOR);
  109. ret =register_chrdev_region(dev,CDD_COUNT,cdd_demo);
  110. }else{
  111. /*动态分配设备号*/
  112. ret =all_chrdev_region(&dev,cdd_minor,CDD_COUNT,cdd_demo);
  113. }
  114. if(ret<0){
  115. prink("register_chrdev_region failed!\n");
  116. goto failure_register_chrdev;
  117. }
  118. /*获取主设备号*/
  119. cdd_major=MAJOR(dev);
  120. /*1*/
  121. cdd_cdevp=kzalloc(sizeof(struct cdd_cdev)*CDD_COUNT,GFP_KERNEL);
  122. if(IS_ERR(cdd_cdevp)){
  123. printk("kzalloc fail");
  124. goto failure_kzalloc;
  125. }
  126. /*创建设备类*/
  127. dev_class = class_create(THIS_MODULE,"cdd_class");
  128. if(IS_ERR(dev_class)){
  129. printk("class_create failed!\n");
  130. ret=PTR_ERR(dev_class);
  131. goto failure_dev_class;
  132. }
  133. for(i=0;i<CDD_COUNT;i++){
  134. /*初始化cdev*/
  135. cdev_init(&(cdd_cdevp[i].cdev),&cdd_fops);
  136. /*添加cdev到内核*/
  137. cdev_add(&(cdd_cdevp[i].cdev),dev+i,1);
  138. /*"/dev/xxx"*/
  139. device_create(dev_class,NULL,dev+i,NULL,"cdd%d",i);
  140. cdd_cdevp[i].led=i;
  141. }
  142. return 0;
  143. failure_dev_class:
  144. kfree(cdd_cdevp);
  145. failure_kzalloc:
  146. unregister_chrdev_region(dev,CDD_COUNT);
  147. failure_register_chrdev:
  148. return ret;
  149. }
  150. void __exit cdd_exit(void){
  151. int i=0;
  152. for(i=0;i<CDD_COUNT;i++){
  153. device_destroy(dev_class,dev+i);
  154. cdev_del(*(cdd_cdevp[i].cdev));
  155. }
  156. /*从内核中删除设备类*/
  157. class_destroy(dev_class);
  158. kfree(cdd_cdevp);
  159. /*注销设备号*/
  160. unregister_chrdev_region(dev,CDD_COUNT);
  161. }
  162. module_init(cdd_init);
  163. moudle_exit(cdd_exit);

控制LED
    看原理图,如果想让LED1亮就是在GPC1_3上输出高电平反之输出低电平
    编程使CPU的GPC1_3管脚为输出管脚
                控制该管教输出高电平或者低电平
    cpu芯片手册:寄存器
                GPC1CON
                GPC1DAT
                GPC1PUD
在linux下控制相应的GPIO管脚,有两种方式
    1)直接操作相应的寄存器
    2)通过内核提供的GPIO操作库函数
在用户空间采用两种方式控制LED的亮/灭
    1)    /dev/led0    /dev/led1
            cdev0        cdev1
            fd0=open("dev/led0",O_RDWR);
            ioctl(fd,cmd,data)
                例:ioctl(fd1,1,0)//亮    ioctl(fd1,0,0)//灭
    2)/dev/led
        cdev
        fd0=open("dev/led0",O_RDWR);
            ioctl(fd,cmd,data)
                例:ioctl(fd1,1,1)//第一个亮    ioctl(fd1,0,2)//第二个灯亮
    
  1. #include<linux/init.h>
  2. #include<linux/module.h>
  3. #include<linux/fs.h>
  4. #include<linux/cdev.h>
  5. #inlcude<linux/device.h>
  6. #include<asm/uaccess.h>//用户空间拷贝头文件
  7. //动态申请的头文件
  8. #include<linux/slab.h>
  9. #include<liunx/string.h>
  10. //GPIO
  11. #include<asm/gpio.h>
  12. #include<plat/gpio-cfg,h>
  13. MODULE_LICENSE("GPL");
  14. #define CDD_MAJOR 200
  15. #define CDD_MINOR 0
  16. #define CDD_COUNT 1
  17. dev_t dev=0;
  18. u32 cdd_major=0;
  19. u32 cdd_minor=0;
  20. /*定义cdev类型的变量*/
  21. struct cdev cdd_cdev;
  22. struct class *dev_class=NULL;
  23. //struct device *dev_device=NULL;
  24. #define BUF_SIZE 100
  25. struct cdd_cdev{
  26. struct cdev cdev;
  27. struct device *dev_device;
  28. u8 led;
  29. char kbuf[BUF_SIZE];
  30. u32 data_len;//记录缓冲区已写入数据的长度
  31. }
  32. struct cdd_cdev *cdd_cdevp=NULL;
  33. unsigned long led_gpio_table[2]={
  34. S5PV210_GPC1(3),//宏->数字
  35. S5PV210_GPC1(4)
  36. };
  37. int cdd_open(struct inode *inode,struct file *filp){
  38. /*2*/
  39. printk("enter cdd_open\n");
  40. struct cdd_cdev* pcdevp=NULL;
  41. printk("enter cdd_open\n");
  42. pcdevp = container_of(inode->i_cdev,struct cdd_cdev,cdev);
  43. printk("led=%d\n",pcdevp->led);
  44. filp->private_data = pcdevp;
  45. return 0;
  46. }
  47. int cdd_read(struct file* filp,char __user *buf,size_t count,
  48. loff_t *offset){
  49. int ret=0;
  50. u32 pos=*offset;
  51. u32 cnt=count;
  52. struct cdd_cdev* pcdevp = filp->private_data;
  53. printk("LED%d\n",pcdevp->led);
  54. //copy_to_user(to,from,n);
  55. //先判断下有没有数据可以进行读取
  56. if(cnt>(pcdevp->data_len/*先有数据*/ - pos/**/)){
  57. cnt=pcdevp->data_len-pos;
  58. }
  59. ret=copy_to_user(buf,pcdevp->kbuf+pos,cnt);
  60. *offset += cnt;
  61. return ret;
  62. }
  63. int cdd_write(struct file *filp,const char __user *buf,
  64. size_t count,loff_t *offset){
  65. printk("enter cdd_write!\n");
  66. int ret=0;
  67. u32 pos=*offset;
  68. u32 cnt =count;
  69. if(cnt > (BUF_SIZE - pos)){
  70. cnt = BUF_SIZE - pos;
  71. }
  72. copy_from_user(pcdevp->kbuf+pos,buf,cnt);
  73. *offset += cnt;
  74. if(*offset > pcdevp->data_len){
  75. pcdevp->data_len = *offset
  76. }
  77. return ret;
  78. }
  79. int cdd_ioctl(struct inode *inode,struct file *filp,
  80. unsigned int cmd ,unsigned data){
  81. printk("enter cdd_ioctl!\n");
  82. switch(cmd){
  83. case 1:/*点亮灯*/
  84. /*
  85. *gpio_direction_output(int gpio,int v);
  86. *设置管脚为输出功能
  87. *gpio要设置的管教编号
  88. *v 默认的高低电平 1高 0低
  89. */
  90. gpio_direction_output(led_gpio_table[data],0);
  91. /*禁止内部上拉*/
  92. s3c_gpio_setpull(led_gpio_table[data],S3C_GPIO_PULL_UP);
  93. /*设置输出值*/
  94. gpio_set_value(led_gpio_table[data],1);
  95. break;
  96. case 0:/*熄灭灯*/
  97. /*设置管脚为输出功能*/
  98. gpio_direction_output(led_gpio_table[data],0);
  99. /*禁止内部上拉*/
  100. s3c_gpio_setpull(led_gpio_table[data],S3C_GPIO_PULL_UP);
  101. /*设置输出值*/
  102. gpio_set_value(led_gpio_table[data],0);
  103. break;
  104. default:
  105. return -EINVAL;
  106. }
  107. return 0;
  108. }
  109. int cdd_release(struct inode *inode,struct file *filp){
  110. printk("enter cdd_release!\n");
  111. return 0;
  112. }
  113. loff_t cdd_llseek(struct file *filp,loff_t offset,int whence){
  114. struct cdd_cdev *pcdevp=filp->private_data;
  115. loff_t newpos=0l
  116. switch(whence){
  117. case SEEK_SET:
  118. newpos=offset;
  119. break;
  120. case SEEK_CUR:
  121. newpos=filp->fops+offset;
  122. break;
  123. case SEEK_END:
  124. newpos=pcdevp->data_len+offset;
  125. break;
  126. default:
  127. return -EINVAL;
  128. }
  129. if(newpos<0 || newpos>=BUF_SIZE){
  130. return -EINVAL;
  131. }
  132. filp->f_ops=newpos;
  133. return newpos;
  134. }
  135. struct file_operations cdd_fops={
  136. .owner = THIS_MODULE,
  137. .open=cdd_open,
  138. .read=cdd_read,
  139. .write=cdd_write,
  140. .ioctl=cdd_ioctl,
  141. .release=cdd_release,
  142. .llseek=cdd_llseek,
  143. };
  144. int __init cdd_init(void){
  145. int ret =0;
  146. int i;
  147. if(cdd_major){
  148. //静态方式分配设备号
  149. dev=MKDEV(CDD_MAJOR,CDD_MINOR);
  150. ret =register_chrdev_region(dev,CDD_COUNT,cdd_demo);
  151. }else{
  152. /*动态分配设备号*/
  153. ret =all_chrdev_region(&dev,cdd_minor,CDD_COUNT,cdd_demo);
  154. }
  155. if(ret<0){
  156. prink("register_chrdev_region failed!\n");
  157. goto failure_register_chrdev;
  158. }
  159. /*获取主设备号*/
  160. cdd_major=MAJOR(dev);
  161. /*1*/
  162. cdd_cdevp=kzalloc(sizeof(struct cdd_cdev)*CDD_COUNT,GFP_KERNEL);
  163. if(IS_ERR(cdd_cdevp)){
  164. printk("kzalloc fail");
  165. goto failure_kzalloc;
  166. }
  167. /*创建设备类*/
  168. dev_class = class_create(THIS_MODULE,"cdd_class");
  169. if(IS_ERR(dev_class)){
  170. printk("class_create failed!\n");
  171. ret=PTR_ERR(dev_class);
  172. goto failure_dev_class;
  173. }
  174. for(i=0;i<CDD_COUNT;i++){
  175. /*初始化cdev*/
  176. cdev_init(&(cdd_cdevp[i].cdev),&cdd_fops);
  177. /*添加cdev到内核*/
  178. cdev_add(&(cdd_cdevp[i].cdev),dev+i,1);
  179. /*"/dev/xxx"*/
  180. device_create(dev_class,NULL,dev+i,NULL,"cdd%d",i);
  181. cdd_cdevp[i].led=i;
  182. }
  183. return 0;
  184. failure_dev_class:
  185. kfree(cdd_cdevp);
  186. failure_kzalloc:
  187. unregister_chrdev_region(dev,CDD_COUNT);
  188. failure_register_chrdev:
  189. return ret;
  190. }
  191. void __exit cdd_exit(void){
  192. int i=0;
  193. for(i=0;i<CDD_COUNT;i++){
  194. device_destroy(dev_class,dev+i);
  195. cdev_del(*(cdd_cdevp[i].cdev));
  196. }
  197. /*从内核中删除设备类*/
  198. class_destroy(dev_class);
  199. kfree(cdd_cdevp);
  200. /*注销设备号*/
  201. unregister_chrdev_region(dev,CDD_COUNT);
  202. }
  203. module_init(cdd_init);
  204. moudle_exit(cdd_exit);
应用程序中调用ioctl就可以实现LED的亮灭
/*
!arm :历史命令就是 在shell中调用最近一次的arm开头指令
*/
上述程序会出现一大堆信息,但是还是能够实现亮和灭

多个设备使用LED的时候会发生穿线的现象
led_open中添加
gpio_request(led_gpiotable[0],"GPC1_3");
led_release中添加
gpio_free(led_gpiotable[0])

驱动程序的调试
    printk
    Oops: 
    kgdb
内核奔溃前会打印关键信息
pc:[<bf0000b8>]    ->    地址
##cat /proc/kallsyms >test.txt
代码崩溃在cdd.c文件的哪个函数中
3G-4G内核地址:模块编译是没有在3-4G中,编译进zImage才是3-4G
 arm-linux-objdump -D cdd.ko >test2.txt
 arm-linux-objdump -D vmlinux >test3.txt

    编译地址    
 
















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值