linux字符设备驱动helloword

来源:互联网 发布:app软件开发软件 编辑:程序博客网 时间:2024/05/16 10:23


linux版本 ubuntu12.04LTS


//驱动部分

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/vmalloc.h>
#include <linux/version.h>
#include <linux/slab.h>
#include <linux/ioctl.h>

MODULE_LICENSE("Dual BSD/GPL");
static struct hello_dev{
 dev_t devno;
 struct cdev cdev;
};
struct hello_dev *hello =NULL;

static int hello_open(struct inode *inode, struct file *file)
{
 printk(KERN_ALERT "hello open\n");
 return 0;
}

static int hello_close(struct inode *inode, struct file *file)
{
 printk(KERN_ALERT "hello close\n");
 return 0;
}

static long hello_ioctl(struct file *file,unsigned int cmd, unsigned long arg)
{

 int i;
 i=cmd;
 printk("ioctl in driver::cmd=%d.\n", cmd);
 switch(i)
 {
  case 1:printk("ioctl command1 successfully\n");break;
  case 2:printk("ioctl command2 successfully\n");break;
  default:
  {
           printk("ioctl fail\n");
   return -1;
  }
 }
 return 0;
}

static struct file_operations dev_fops = {
    .owner = THIS_MODULE,
    .open = hello_open,
    .unlocked_ioctl = hello_ioctl,
    .release = hello_close,
};

static int hello_init(void)
{
 int ret;
 dev_t devno;
 devno=MKDEV(235,0);//静态获得设备号,注意不要和原来的设备号冲突
 //ret=alloc_chrdev_region(&devno,0,1,"hello");//动态获得设备号
 ret= register_chrdev_region(devno,1,"hello");
 if(ret<0)
 {
  printk(KERN_WARNING "HEHE\n");
  return 0;
 }
 hello = kmalloc(sizeof(struct hello_dev),GFP_KERNEL);
 memset(hello,0,sizeof(struct hello_dev));

 hello->devno = devno;

 cdev_init(&hello->cdev,&dev_fops);//把获得的设备号与驱动的方法关联起来
 hello->cdev.owner = THIS_MODULE;

 ret =cdev_add(&hello->cdev,devno,1);//这句我的理解是,把设备号与驱动方法关联到内核中
 if(ret)
 {
  printk(KERN_NOTICE "Error %d.\n",ret);
  return 0;
 }

    printk(KERN_ALERT "Hello, world\n");
    return 0;
}

static void hello_exit(void)
{
 dev_t devno =hello->devno;
 
 cdev_del(&hello->cdev);
 if(hello)
 {
  kfree(hello);
 }
 unregister_chrdev_region(devno,1);
    printk(KERN_ALERT "Goodbye, cruel world\n");
}

module_init(hello_init);
module_exit(hello_exit);

下面是调试代码:就是调试一下驱动写的方法有没有实现。

#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<linux/rtc.h>
#include<linux/ioctl.h>
#include<stdio.h>
#include<stdlib.h>

#define TEXT 2
int main()
{
 int fd;
 int i;
 unsigned int j;
 int retval;
 fd=open("/dev/hello",O_RDWR);
 if(fd==-1)
 {
  perror("error open\n");
  exit(-1);
 }
 printf("open success\n");
 j=1;
 retval=ioctl(fd,j,0);
 if(retval==-1)
 {
  perror("ioctl error\n");
  exit(-1);
 }
 printf("send command1 successfully\n");
 retval=ioctl(fd,2,0);
 if(retval==-1)
 {
  perror("ioctl error\n");
  exit(-1);
 }
 printf("send command2 successfully\n");

 close(fd);
}

在本机调试,驱动的makefile如下:

ifneq ($(KERNELRELEASE),)  

     obj-m += char_hello.o  //这个与你的文件名有关  

else  

     KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build  

     PWD := $(shell pwd)  

default:  

     $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

clean:  

     $(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean  

endif 

加载驱动,首先要编译生成.ko文件

insmod char_hello.ko

通过lsmod就能看到加载了内核 或者 dmesg | tail -10可以看到加载了驱动中_init函数printk的信息

然后用mknod -m 0666(权限) /dev/hello c 235 0   //c代表字符设备 235 是主设备号,0是此设备号 这里我用了静态指定设备号,如果用动态的话,就先在终端执行cat /proc/devices查看

删除驱动就用rmmod  char_hello

测试程序直接用gcc编译执行就行了。

编者在学习期间遇到一个奇怪的问题,在PC机测试过程中,驱动中的ioctl函数里面,当驱动收到cmd=2的话,是不会进入函数体的,终端会显示地址错误的提示,编者更改测试程序中传的值,尝试过10个左右不同的数字,除了“2”,其他都是正常的。编者忽略这个问题,直接把代码移植到开发板上。发觉丝毫没有问题。

我和我的小伙伴都惊呆了。

这问题,编者一直没想出个所以然。

0 0