1. 程式人生 > >linux字元裝置基本框架的搭建

linux字元裝置基本框架的搭建

        在學習linux字元裝置驅動的時候,我發現其中需要用到的函式比較多,而我經常是記不住其中各種函式的引數,就在這裡整理一下,加深一下對字元裝置框架理解。

        首先就從基礎的開始,就是模組許可宣告,載入函式和解除安裝函式的預設模式:

        MODULE_LICENSE("GPL");

        int init_module(void);                               載入函式的預設模式

        void cleanup_module(void);                   解除安裝函式的預設模式

        非預設模式下的載入和解除安裝可以寫成這樣:

        static int __init xxx_mmt_init(void);

        static void __exit xxx_mmt_exit(void);

        module_init();

        module_exit();

        模組引數和模組匯出符號,這兩個可以不用:

        module_param(引數名,引數型別,引數讀/寫許可權)

        EXPORT_SYMBOL(符號名);

        註冊字元驅動裝置相關操作:

        關於裝置號的建立就不再說了,比較簡單,然後是動態註冊裝置:

        alloc_chrdev_region()

        動態註冊一般不常用,主要記一下靜態註冊:

        register_chrdev_region(devno, number_of_devices, "xxx");

        之後是登出裝置號:

        unregister_chrdev_region(devno, number_of_devices);

        我自己在練習驅動編寫的時候,之前會碰到這樣的問題,把dev_t devno = MKDEV(xxx_major, xxx_minor);當成全域性變數來定義的時候編譯器會報錯,後來問過之後知道MKDEV作為一個c語言函式是需要類似於main()函式入口才能執行的,所以我們可以這樣做,把dev_t devno放在全域性變數那裡,然後MKDEV放在載入函式和解除安裝函式裡面就不會有問題了。

        與操作集合file_operations有關的函式:

        static int xxx_open(struct inode *inode, struct file *filp);

        static int xxx_release(struct inode *inode, struct file *filp);

        static ssize_t xxx_read(struct file *filp, const char *buf, size_t count, loff_t *fops);

        static ssize_t xxx_write(struct file *filp, const char *buf, size_t count, loff_t *fops);

        下面是實現對裝置的操作集合,你用了哪個函式,就把那個函式加入到操作集合裡面,具體實現情況如下:

        struct  file_operations xxx_fops = {

                        .owner = THIS_MODULE,

                        .open = xxx_open,

                        .release = xxx_release,

                        .read = xxx_read,

                        .write = xxx_write,

        };

        對於我們來說,瞭解到驅動整體的框架的話,我們現在所要才去的操作就是把操作集合跟要想註冊的裝置聯絡起來,那麼我們就需要這個struct cdev這個結構體來實現,結構體具體作用和用法如下:

        struct cdev cdev;       首先是定義一個cdev結構體;

        cdev_init(&cdev, &xxx_fops);             初始化函式,把cdev跟file_operations聯絡在一起;

        cdev_add(&cdev, devno, 1);              把裝置號加入到cdev裡面;

        就這樣我們就把裝置號跟操作集合聯絡在一起了,其實這裡面的聯絡還是比較複雜,在這裡就不在深入探討了。

        read中驅動會呼叫copy_to_user()將資料返回給應用層,而且用這個函式時,最好定義一個變數去接受它的返回值,不然的話編譯器會報警告:

        int ret;

        ret = copy_to_user(buf,data,count);

        write中,我們也可以呼叫copy_from_user()將資料從應用層接受過來:

        int ret;

        ret = copy_from_user(data,buf,count);

        大體上就是這樣,一個字元裝置驅動的基本框架就構建起來了,然後再來一個例子讓我加深一下理解:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>

MODULE_LICENSE ("GPL");

int hello_major = 250;
int hello_minor = 0;
int number_of_devices = 1;
char data[128] = "foobar not equal to barfoo";

struct cdev cdev;

static int hello_open (struct inode *inode, struct file *file)
{
  printk (KERN_INFO "Hey! device opened\n");
  
  return 0;
}

static int hello_release (struct inode *inode, struct file *file)
{
  printk (KERN_INFO "Hmmm... device closed\n");
  
  return 0;
}

ssize_t hello_read (struct file *filp, char *buff, size_t count, loff_t *offp)
{
  ssize_t result = 0;
  
  if (count < 0) return -EINVAL;
  if (count > 127) count = 127;

  if (copy_to_user (buff, data, count)) 
  {
    result = -EFAULT;
  }
  else
  {
    printk (KERN_INFO "wrote %d bytes\n", count);
	result = count;
  }
   
  return result;
}


struct file_operations hello_fops = {
  .owner = THIS_MODULE,
  .open  = hello_open,
  .release = hello_release,
  .read  = hello_read,
};

static void char_reg_setup_cdev (void)
{
  int error;
  dev_t devno;
  
  devno = MKDEV (hello_major, hello_minor);
  cdev_init (&cdev, &hello_fops);
  cdev.owner = THIS_MODULE;
  error = cdev_add (&cdev, devno , 1);
  if (error)
    printk (KERN_NOTICE "Error %d adding char_reg_setup_cdev", error);
}

static int __init hello_2_init (void)
{
  int result;
  dev_t devno;

  devno = MKDEV (hello_major, hello_minor);
  result = register_chrdev_region (devno, number_of_devices, "hello");

  if (result < 0) {
    printk (KERN_WARNING "hello: can't get major number %d\n", hello_major);
    return result;
  }

  char_reg_setup_cdev ();
  printk (KERN_INFO "char device registered\n");
  
  return 0;
}

static void __exit hello_2_exit (void)
{
  dev_t devno = MKDEV (hello_major, hello_minor);
  
  cdev_del (&cdev);

  unregister_chrdev_region (devno, number_of_devices);
}

module_init (hello_2_init);
module_exit (hello_2_exit);