天天看點

linux字元裝置驅動helloword



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”,其他都是正常的。編者忽略這個問題,直接把代碼移植到開發闆上。發覺絲毫沒有問題。

我和我的小夥伴都驚呆了。

這問題,編者一直沒想出個是以然。