技術(shù)員聯(lián)盟提供win764位系統(tǒng)下載,win10,win7,xp,裝機純凈版,64位旗艦版,綠色軟件,免費軟件下載基地!

當(dāng)前位置:主頁 > 教程 > 服務(wù)器類 >

Linux字符設(shè)備驅(qū)動編寫基本流程

來源:技術(shù)員聯(lián)盟┆發(fā)布時間:2018-08-20 12:27┆點擊:

  Linux下的MISC簡單字符設(shè)備驅(qū)動雖然使用簡單,但卻不靈活。

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

  ---分配和釋放設(shè)備編號

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

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

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

  ---文件操作file_operations結(jié)構(gòu)體,內(nèi)部連接了多個設(shè)備具體操作函數(shù)。該變量內(nèi)部的函數(shù)指針指向驅(qū)動程序中的具體操           作,沒有對應(yīng)動作的指針設(shè)置為NULL。

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

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

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

  返回新位置。

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

  中讀取數(shù)據(jù),讀取成功返回讀取的字節(jié)數(shù)。

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

  寫入數(shù)據(jù),如果成功返回寫入的字節(jié)數(shù)。

  5)int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);ioctl系統(tǒng)調(diào)用提供

  發(fā)出設(shè)備特定命令的方法。

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

  7)int (*release) (struct inode *, struct file *);釋放文件結(jié)構(gòu)函數(shù)指針。

  一般初始化該結(jié)構(gòu)體如下:

  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:以上的文件操作函數(shù)指針并不是全部,只是介紹了幾個常用的操作。

  ---文件結(jié)構(gòu)

  struct file定義在linux/fs.h中,是設(shè)備驅(qū)動中第二個最重要的數(shù)據(jù)結(jié)構(gòu),此處的file和

  用戶空間程序中的FILE指針沒有關(guān)系。前者位于內(nèi)核空間,后者位于用戶控件。

  文件結(jié)構(gòu)代表一個打開的文件。(他不特定給設(shè)備驅(qū)動;系統(tǒng)中每個打開的文件

  有一個關(guān)聯(lián)的struct file在內(nèi)核空間)。它由內(nèi)核在open時創(chuàng)建,并可以傳遞給文件件

  操作函數(shù),文件關(guān)閉之后,內(nèi)核釋放數(shù)據(jù)結(jié)構(gòu)。

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

  2)loff_t f_ops。當(dāng)前讀寫位置

  3)unsigned int f_flags 。文件標(biāo)志,O_RDONLY、O_NONBLOCK,

  4)struct file_operations *f_op。關(guān)聯(lián)文件相關(guān)操作

  5)void *private_data。open系統(tǒng)調(diào)用設(shè)置該指針NULL,指向分配的數(shù)據(jù)。

  6)struct dentry *f_dentry。關(guān)聯(lián)到文件的目錄入口dentry結(jié)構(gòu)。

  ---inode結(jié)構(gòu)

  inode結(jié)構(gòu)由內(nèi)核在內(nèi)部用來表示文件。它和代表打開文件描述符的文件結(jié)構(gòu)是不

  同的。inode結(jié)構(gòu)包含大量關(guān)于文件的信息。作為通用規(guī)則,這個結(jié)構(gòu)只有兩個成

  員對驅(qū)動代碼有作用。

  dev_t i_rdev。對于代表設(shè)備文件的節(jié)點,這個成員包含實際的設(shè)備編號。

  struct cdev *i_cdev。內(nèi)核內(nèi)部結(jié)構(gòu),代表字符設(shè)備。

  ---字符設(shè)備注冊

  在內(nèi)核調(diào)用你的設(shè)備操作前,你編寫分配并注冊一個或幾個struct cdev.

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

  或者定義成static均可。

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

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

  最后告訴內(nèi)核該cdev。

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

  /*上述總結(jié),到此關(guān)于設(shè)備文件相關(guān)的結(jié)構(gòu)數(shù)據(jù)以及如何注冊銷毀等操作相關(guān)的

  函數(shù)基本上都已經(jīng)介紹完畢。主要的還是要設(shè)計具體操作的函數(shù)來實現(xiàn)具體的

  邏輯操作*/

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

  #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");

  return err;

  }

  major = MAJOR(leds_cdev.dev);

  major = MINOR(leds_cdev.dev);

  dev_number = leds_cdev.dev;

  }

  ret = cdev_add(&leds_cdev,dev_number, DEVICE_COUNT);

  leds_class = class_create (THIS_MODULE, DEVICE_NAME);

  device_create (leds_class, NULL, dev_number, NULL, DEVICE_NAME);

  return ret;

  }

  static void leds_init_gpm(int leds_default){

  int tmp = 0;

  tmp = ioread32(S3C64XX_GPMCON);

  tmp &= (~0xffff);

  tmp |= 0x1111;

  iowrite32(tmp,S3C64XX_GPMCON);

  tmp = ioread32(S3C64XX_GPMPUD);

  tmp &= (~0XFF);

  tmp |= 0xaa;

  iowrite32(tmp,S3C64XX_GPMPUD);

  tmp = ioread32(S3C64XX_GPMDAT);

  tmp &= (~0xf);

  tmp |= leds_default;

  iowrite32(tmp, S3C64XX_GPMDAT);

  }

  static leds_init( void)

  {

  int ret;

  ret = leds_create_device();

  leds_init_gpm (~leds_state);

  printk(DEVICE_NAME"tinitializedn");

  return ret;

  }

  static void leds_destroy_device(void)

  {

  device_destroy(leds_class, dev_number);

  if(leds_class)

  class_destroy(leds_class);

  unregister_chrdev_region(dev_number, DEVICE_NAME);

  }

  static void leds_exit(void)

  {

  leds_destroy_device();

  printk(DEVICE_NAME"texitn");

  }

  module_init(leds_init);

  module_exit(leds_exit);

  module_param(leds_state, int, S_IRUGO|S_IWUSR);

  module_param_array(params, charp, ?m_size, S_IRUGO|S_IWUSR);

  MODULE_LICENSE("GPL");

  MODULE_AUTHOR("lining");