天天看點

linux驅動學習筆記1--字元型驅動的編寫

本文用兩個char型數組來模拟字元型裝置,驅動源碼如下:

#include <linux/module.h>

#include <linux/fs.h>

#include <linux/init.h>

#include <linux/cdev.h>

#include <asm/uaccess.h>

int dev1_registers[5];  //兩個數組模拟兩個字元型裝置

int dev2_registers[5];

struct cdev cdev;    //定義一個裝置變量

dev_t devno;          //定義一個裝置号變量

int mem_open(struct inode *inode, struct file *filp)

{

    int num = MINOR(inode->i_rdev);

    if (num==0)

        filp->private_data = dev1_registers;       //把裝置位址指派給filp->private_data,在下面的read函數裡需要調用設                                                                       //備的位址

    else if(num == 1)

        filp->private_data = dev2_registers;

    else

        return -ENODEV;  //無效的次裝置号

    return 0; 

}

int mem_release(struct inode *inode, struct file *filp)

{

  return 0;

}

static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)

{

  unsigned long p =  *ppos;

  unsigned int count = size;

  int ret = 0;

  int *register_addr = filp->private_data;       //在open函數裡,已經把裝置的位址指派給了filp->private_data

  if (p >= 5*sizeof(int))

    return 0;

  if (count > 5*sizeof(int) - p)

    count = 5*sizeof(int) - p;

  if (copy_to_user(buf, register_addr+p, count))

  {

    ret = -EFAULT;

  }

  else

  {

    *ppos += count;

    ret = count;

  }

  return ret;

}

static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)

{

  unsigned long p =  *ppos;

  unsigned int count = size;

  int ret = 0;

  int *register_addr = filp->private_data;      //在open函數裡,已經把裝置的位址指派給了filp->private_data

  if (p >= 5*sizeof(int))

    return 0;

  if (count > 5*sizeof(int) - p)

    count = 5*sizeof(int) - p;

  if (copy_from_user(register_addr + p, buf, count))

    ret = -EFAULT;

  else

  {

    *ppos += count;

    ret = count;

  }

  return ret;

}

static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)

    loff_t newpos;

    switch(whence) {

      case SEEK_SET: 

        newpos = offset;

        break;

      case SEEK_CUR: 

        newpos = filp->f_pos + offset;

        break;

      case SEEK_END: 

        newpos = 5*sizeof(int)-1 + offset;

        break;

      default: 

        return -EINVAL;

    }

    if ((newpos<0) || (newpos>5*sizeof(int)))

    return -EINVAL;

    filp->f_pos = newpos;

    return newpos;

}

static const struct file_operations mem_fops =

{

  .llseek = mem_llseek,

  .read = mem_read,

  .write = mem_write,

  .open = mem_open,

  .release = mem_release,

};

static int memdev_init(void)

{

  cdev_init(&cdev, &mem_fops);

  alloc_chrdev_region(&devno, 0, 2, "memdev");  //注冊兩個裝置,此裝置号的起始值為0

  cdev_add(&cdev, devno, 2);                              //向系統添加裝置

}

static void memdev_exit(void)

{

  cdev_del(&cdev);  

  unregister_chrdev_region(devno, 2);

}

MODULE_LICENSE("GPL");

module_init(memdev_init);

module_exit(memdev_exit);

對應的應用程式如下:(write-mem.c為寫裝置,read-mem.c為讀裝置)

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

int main()

{

int fd = 0;

int src = 2013;

fd = open("/dev/memdev0",O_RDWR);

write(fd, &src, sizeof(int));

close(fd);

return 0;

}

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

int main()

{

int fd = 0;

int dst = 0;

fd = open("/dev/memdev0",O_RDWR);

read(fd, &dst, sizeof(int));

printf("dst is %d\n",dst);

close(fd);

return 0;

}

 整套驅動及應用軟體的開發流程如下:

1.寫好驅動程式

2.為驅動寫Makefile,且Makefile檔案要與驅動放在同一目錄下

3.執行make指令,将驅動memdev.c編譯成memdev.ko

4.執行指令insmod memdev.ko加載驅動到核心

  執行指令lsmod可以檢視到加載的所有驅動

  執行指令rmmodmemdev.ko解除安裝驅動  

5.為裝置建立裝置檔案

  執行指令mknod/dev/裝置名 c 主裝置号 次裝置号

  參數說明:

          裝置名可以自己定義  (裝置名是在應用程式在調用open活write時要用的,例              如fd=open("/dev/memdev0",O_RDWR),系統根據裝置名取找到相應的驅動)

          c表示此裝置為字元型裝置(b表示為塊裝置)  

          主裝置号在本例中是系統自己配置設定的,通過指令cat/proc/devices可以檢視驅動的主裝置号

次裝置号也是使用者自己定義,範圍必須在[0,255]

本例中的具體指令:mknod /dev/memdev0 c 123 0   (假設檢視到的是123)

由于本例中注冊了兩個裝置

 alloc_chrdev_region(&devno, 0, 2, "memdev");

 cdev_add(&cdev, devno, 2);

由于注冊了兩個裝置,是以需要建立兩個裝置檔案

現在建立第二個裝置檔案mknod /dev/memdev1 c 123 1

建立第二個裝置檔案時要保證第二個裝置檔案的名字和第一個裝置檔案名字不同,且次裝置号加1

驅動中open函數的原型如下:

int mem_open(struct inode *inode, struct file *filp)

{

    int num =MINOR(inode->i_rdev);

    if (num==0)

       filp->private_data = dev1_registers; //把裝置1的位址指派給//filp->private_data

    else if(num== 1)

       filp->private_data = dev2_registers;//把裝置2的位址指派//filp->private_data

    else

        return-ENODEV;  //無效的次裝置号

    return 0;

}

當應用程式調用open("/dev/memdev0",O_RDWR)時,打開的是裝置檔案memdev0,對應的次裝置号為0, 是以num=0;

當應用程式調用open("/dev/memdev1",O_RDWR)時,打開的是裝置檔案memdev1,對應的次裝置号為1,是以num=1。

具體Makefile如下:    

obj-m := memdev.o

KDIR := /usr/src/linux-headers-3.2.0-24-generic-pae

all:

 make -C $(KDIR) M=$(PWD) modules

clean:

    rm -f *.ko*.o *.mod.o *.mod.c *.symvers *.bak *.order

Makefile的說明:

    obj-m 表示将驅動編譯成子產品(modues)

    KDIR 是本系統核心的路徑,因為在編譯驅動時要用到裡面的檔案。若編譯的驅動要運作在其他版本的linux系統上,還需下載下傳其他版本的系統檔案,并把KDIR改為相應版本linux核心的路徑。

    $(KDIR)是對路徑的引用 make -C $(KDIR)等價與 make -C/usr/src/linux-headers-3.2.0-24-generic-pae

    $(PWD)是對目前路徑的引用 ,也是編譯後生成.ko文就存放的路徑

clean :删除編譯過程中産生的中間檔案

繼續閱讀