萬盛學電腦網

 萬盛學電腦網 >> 服務器教程 >> Linux字符設備驅動編寫基本流程

Linux字符設備驅動編寫基本流程

   ---簡介

  Linux下的MISC簡單字符設備驅動雖然使用簡單,但卻不靈活。

  只能建立主設備號為10的設備文件。字符設備比較容易理解,同時也能夠滿足大多數簡單的硬件設備,字符設備通過文件系          統中的名字來讀取。這些名字就是文件系統中的特殊文件或者稱為設備文件、文件系統的簡單結點,一般位於/dev/目錄下          使用ls進行查看會顯示以C開頭證明這是字符設備文件crw--w---- 1 root tty 4, 0 4月 14 11:05 tty0。第一個數字是主設備                號,第二個數字是次設備號。

  ---分配和釋放設備編號

  1)在建立字符設備驅動時首先要獲取設備號,為此目的的必要的函數是register_chrdev_region,在linux/fs.h中聲明:int                register_chrdev_region(dev_t first, unsigned int count, char *name);first是你想要分配的起始設備編號,first的次編號通            常是0,count是你請求的連續設備編號的總數。count如果太大會溢出到下一個主設備號中。name是設備的名字,他會出          現在/proc/devices 和sysfs中。操作成功返回0,如果失敗會返回一個負的錯誤碼。

  2)如果明確知道設備號可用那麼上一個方法可行,否則我們可以使用內核動態分配的設備號int alloc_chrdev_region(dev_t            *dev, unsigned int firstminor,unsigned int count, char *name);dev是個只輸出的參數,firstminor請求的第一個要用的次            編號,count和name的作用如上1)對於新驅動,最好的方法是進行動態分配

  3)釋放設備號,void unregister_chrdev_region(dev_t first unsigned int count);

  ---文件操作file_operations結構體,內部連接了多個設備具體操作函數。該變量內部的函數指針指向驅動程序中的具體操           作,沒有對應動作的指針設置為NULL。

  1)fops的第一個成員是struct module *owner 通常都是設置成THIS_MODULE。

  linux/module.h中定義的宏。用來在他的操作還在被使用時阻止模塊被卸載。

  2)loff_t (*llseek) (struct file *, loff_t, int);該方法用以改變文件中的當前讀/寫位置

  返回新位置。

  3)ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);該函數用以從設備文件

  中讀取數據,讀取成功返回讀取的字節數。

  4)ssize_t (*write) (struct file *, const char __user *,size_t , loff_t *);該函數用以向設備

  寫入數據,如果成功返回寫入的字節數。

  5)int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);ioctl系統調用提供

  發出設備特定命令的方法。

  6)int (*open) (struct inode *, struct file *);設備文件進行的第一個操作,打開設備文件。

  7)int (*release) (struct inode *, struct file *);釋放文件結構函數指針。

  一般初始化該結構體如下:

  struct file_operations fops = {

  .owner = THIS_MODULE, .llseek = xxx_llseek, .read = xxx_read, .write = xxx_write,

  .ioctl = xxx_ioctl, .open = xxx_open, .release = xxx_release };

  PS:以上的文件操作函數指針並不是全部,只是介紹了幾個常用的操作。

  ---文件結構

  struct file定義在linux/fs.h中,是設備驅動中第二個最重要的數據結構,此處的file和

  用戶空間程序中的FILE指針沒有關系。前者位於內核空間,後者位於用戶控件。

  文件結構代表一個打開的文件。(他不特定給設備驅動;系統中每個打開的文件

  有一個關聯的struct file在內核空間)。它由內核在open時創建,並可以傳遞給文件件

  操作函數,文件關閉之後,內核釋放數據結構。

  1)mode_t f_mode。確定文件讀寫模式

  2)loff_t f_ops。當前讀寫位置

  3)unsigned int f_flags 。文件標志,O_RDONLY、O_NONBLOCK,

  4)struct file_operations *f_op。關聯文件相關操作

  5)void *private_data。open系統調用設置該指針NULL,指向分配的數據。

  6)struct dentry *f_dentry。關聯到文件的目錄入口dentry結構。

  ---inode結構

  inode結構由內核在內部用來表示文件。它和代表打開文件描述符的文件結構是不

  同的。inode結構包含大量關於文件的信息。作為通用規則,這個結構只有兩個成

  員對驅動代碼有作用。

  dev_t i_rdev。對於代表設備文件的節點,這個成員包含實際的設備編號。

  struct cdev *i_cdev。內核內部結構,代表字符設備。

  ---字符設備注冊

  在內核調用你的設備操作前,你編寫分配並注冊一個或幾個struct cdev.

  struct cdev *my_cdev = cdev_alloc(); my_cdev->ops = &my_fops;

  或者定義成static均可。

  對定義的cdev變量進行初始化,可以使用專門的函數,或者使用如上的方法。

  cdev_init( my_cdev, &my_fops); 其實上邊的兩行代碼就是做了這個函數的工作。

  最後告訴內核該cdev。

  cdev_add(struct cdev *dev, dev_t num, unsigned int count);

  /*上述總結,到此關於設備文件相關的結構數據以及如何注冊銷毀等操作相關的

  函數基本上都已經介紹完畢。主要的還是要設計具體操作的函數來實現具體的

  邏輯操作*/

  以下代碼整理、摘錄自《Android深度探索HAL與驅動開發-李寧》LED驅動篇

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #deifne DEVICE_NAME "s3c6410_leds"

  #define DEVICE_COUNT 1

  #define S3C6410_LEDS_MAJOR 0

  #define S3C6410_LEDS_MINOR 234

  #define PARAM_SIZE 3

  static int major = S3C6410_LEDS_MAJOR;

  static int minor = S3C6410_LEDS_MINOR;

  static dev_t dev_number;

  static int leds_state = 1;

  static char *params[] = {"string1","string2","string3"};

  static iint param_size = PARAM_SIZE;

  static struct class *leds_class = NULL;

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

  {

  switch (cmd)

  {

  unsigned tmp;

  case 0:

  case 1:

  if (arg > 4)

  return -EINVAL;

  tmp = ioread32 (S3C64XX_GPMDAT);

  if (cmd == 1)

  tmp &= (~(1 << arg));

  else

  tmp |= (1 << arg);

  iowrite32 (tmp, S3C64XX_GPMDAT);

  return 0;

  default : return -EINVAL;

  }

  }

  static ssize_t s3c6410_leds_write (struct file *file, const char __user *buf, size_t count, loff_t *ppos)

  {

  unsigned tmp = count;

  unsigned long i = 0;

  memset(mem, 0, 4);

  if (count > 4)

  tmp = 4;

  if (copy_from_user (mem, buf, tmp) )

  return -EFAULT;

  else{

  for( i=0; i<4; i++)

  {

  tmp = ioread32(S3C64XX_GPMDAT);

  if (mem[i] == '1')

  tmp &= (~(1 << i));

  else

  tmp |= (1 << i);

  iowrite32(tmp, S3C64XX_GPMDAT);

  }

  return count;

  }

  }

  static struct file_operations dev_fops =

  {.owner = THIS_MODULE, .unlocked_ioctl = s3c6410_leds_ioctl, .write = s3c6410_leds_write};

  static struct cdev leds_cdev;

  static int leds_create_device(void)

  {

  int ret = 0;

  int err = 0;

  cdev_init (&leds_cdev, &dev_fops);

  leds_cdev.owner = THIS_MODULE;

  if (major > 0)

  {

  dev_number = MKDEV(major,minor);

  err = register_chrdev_region(dev_number, DEVICE_COUNT, DEVICE_NAME);

  if (err < 0)

  {

  printk(KERN_WANRING "register_chrdev_region errorn");

  return err

  }

  }

  else{

  err = alloc_chrdev_region(&leds_cdev.dev, 10, DEVICE_COUNT, DEVICE_NAME);

  if(err < 0)

  {

  printk (KERN_WARNING "alloc_chrdev_region errorn");

  retur

copyright © 萬盛學電腦網 all rights reserved