字符設備的新寫法

前言

之前我寫的字符設備中,用到的分配主設備號的函數是

int register_chrdev(unsigned int major, const char *name,
		    const struct file_operations *fops);

       這種方法是在內核2.6之前比較老版本中常用到的方法,調用register_chrdev後,系統會一次性爲你的主設備分配0~255的次設備號。但是在內核2.6版本後不建議這麼使用了,可能在以後某個版本就沒有這個函數了,所以我們使用新的函數來分配主設備號,這也是這篇文章要講的內容。‘

正文

下面我將直接給出一個示例代碼,它的功能是

(1)創建一個三個字符設備,分別是/dev/hello0、/dev/hello1、/dev/hello2。
它們的主設備號相同,次設備號範圍是0~2
(2)其中次設備號從0~1的字符設備的調用的open函數爲hello_open;而次設備號爲2的
字符設備的open函數爲hello2_open
(3)另外,在分配主設備號和次設備號的情況下,在/dev目錄下,直接創建一個hello3字符設備,看是否能打開

驅動示例代碼如下

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h>
#include <linux/cdev.h>

static struct cdev hello_cdev;
static struct cdev hello2_cdev;

static int major;
static int minor;

#define HELLO_CNT 2
#define HELLO2_CNT 1

static struct class *hello_class;
static struct class_device *hello_class_dev;
static dev_t devid;

static int hello_open(struct inode *inode, struct file *file)
{
	printk(KERN_WARNING "This is %s\n", __FUNCTION__);
	return 0;
}

static int hello2_open(struct inode *inode, struct file *file)
{
	printk(KERN_WARNING "This is %s\n", __FUNCTION__);
	return 0;
}


static struct file_operations hello_fops = {
	.owner = THIS_MODULE,
	.open  = hello_open,
};

static struct file_operations hello2_fops = {
	.owner = THIS_MODULE,
	.open  = hello2_open,
};

static int hello_alloc_major(char *module, int cnt)
{
	int result;
#if 0
	/*下面是老的註冊字符設備的方法。這個方法將會給我們同時分配0~255作爲次設備號,
	 *也就是(major,0~255)的設備都能調用hello_fops
	 */
	major = register_chrdev(0, "hello", &hello_fops);
#else
	if (major && minor) {
		printk("major = %d, minor = %d\n", major, minor);
		devid=  MKDEV(major, minor);
		/*如果已知主設備號,可以使用下面的函數,但是不推薦這麼做,最好能動態分配主設備號。
		 *下面我們分配的cnt個次設備號,也就是(major,0~(cnt-1))的設備都對應hello_fops,
		 *(major,cnt~255)的設備都不對應hello_fops
		 */
		result = register_chrdev_region(devid, cnt, module);
	} else {
		/*下面的函數就是能動態分配主設備號
		 *同理,(major,0~(cnt-1))的設備都對應hello_fops
		 */
		result = alloc_chrdev_region
(&devid, 0, cnt, module);
		major = MAJOR(devid);
		printk
("major = %d\n", major);
	}

	if (result < 0) {
		printk(KERN_WARNING "%s : cant not get major %d\n", module, major);
		return result;
	}
#endif

	return 0;
}

static void hello_setup_cdev(struct cdev *mycdev, struct file_operations *my_fops, int cnt)
{
	int err;
	/*分配和初始化我們的cdev結構體*/
	cdev_init(mycdev, my_fops);
	/*將我們的cdev告訴內核*/
	err = cdev_add(mycdev, devid, cnt);
	if (err)
		printk(KERN_WARNING "Error %d adding hello\n", err);
}

static int hello_init(void)
{
	int err, result;

	/*先分配兩個次設備(major, 0~1),對應hello_fops操作*/
	result = hello_alloc_major("hello", HELLO_CNT);
	if (result < 0)
		return result;
	hello_setup_cdev(&hello_cdev, &hello_fops, HELLO_CNT);

	/*再分配一個次設備(major, 2),對應hello2_fops操作*/
	minor = 2;
	result = hello_alloc_major("hello2", HELLO2_CNT);
	if (result < 0)
		return result;
	hello_setup_cdev(&hello2_cdev, &hello2_fops, HELLO2_CNT);
	
	hello_class = class_create(THIS_MODULE, "hello_class"); /*/sys/class/hello_class/*/
	class_device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello0"); /*/dev/hello0*/
	class_device_create(hello_class, NULL, MKDEV(major, 1), NULL, "hello1"); /*/dev/hello1*/
	class_device_create(hello_class, NULL, MKDEV(major, 2), NULL, "hello2"); /*/dev/hello2*/
	/*因爲我們只分配了兩個次設備號,所以當我們想打開下面的文件時,肯定會失敗*/
	class_device_create(hello_class, NULL, MKDEV(major, 3), NULL, "hello3"); /*/dev/hello3*/

	return 0;
	
}

static void hello_exit(void)
{
	class_device_destroy(hello_class, MKDEV(major, 0));
	class_device_destroy(hello_class, MKDEV(major, 1));
	class_device_destroy(hello_class, MKDEV(major, 2));
	class_device_destroy(hello_class, MKDEV(major, 3));
	class_destroy(hello_class);
	cdev_del(&hello_cdev);
	unregister_chrdev_region(MKDEV(major, 0), HELLO_CNT);
	cdev_del(&hello2_cdev);
	unregister_chrdev_region(MKDEV(major, 2), HELLO2_CNT);	
}


module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

       和我之前寫的用老方法分配主設備的驅動程序(https://blog.csdn.net/lee_jimmy/article/details/82726579)相比,現在主要的區別就是分爲了兩步:

(1)調用register_chrdev_region或者alloc_chrdev_region函數來分配主設備號,
並且規定了需要分配多少個次設備號,不再是以前的默認分配255個次設備號
(2)根據第一步中分配到的主次設備號,分別調用cdev_init和cdev_add函數,將我
們的struct cdev和file_operations結構體註冊進內核

       可以看得出,以前用register_chrdev函數註冊,現在已經分成了上面兩步,雖然麻煩了點,但是做得更金細化了。至於函數怎麼用,都什麼含義我就不再詳細講解了,代碼結合註釋應該很容易看得懂。

編譯和測試

下面給出編譯用的Makefile文件:

測試程序:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>

void print_usage(char *file)
{
	printf("%s <dev>\n", file);
}

int main(int argc, char *argv[])
{
	int fd;
	if (argc != 2) {
		print_usage(argv[0]);
		return -1;
	}
	fd = open(argv[1], O_RDONLY);
	if (fd < 0)
		printf("can not open %s\n", argv[1]);
	else
		printf("can open %s\n", argv[1]);
	

	return 0;
}

 測試結果:

(1)insmod hello.ko插入我們編譯的模塊

# insmod hello.ko 
major = 252
major = 252, minor = 2


(2)用下面的命令可以看到我們註冊的兩個模塊hello和hello2
# cat /proc/devices 
Character devices:
  1 mem
  2 pty
  3 ttyp
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  6 lp
  7 vcs
 10 misc
 13 input
 14 sound
 29 fb
 90 mtd
 99 ppdev
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
204 s3c2410_serial
252 hello
252 hello2
253 usb_endpoint
254 rtc


(3)
# ls /dev/hello* -l
crw-rw----    1 0        0        252,   0 Jan  1 00:00 /dev/hello0
crw-rw----    1 0        0        252,   1 Jan  1 00:00 /dev/hello1
crw-rw----    1 0        0        252,   2 Jan  1 00:00 /dev/hello2
crw-rw----    1 0        0        252,   3 Jan  1 00:00 /dev/hello3

(4)
# ./hello_test /dev/hello0
This is hello_open  //調用的hello_open函數
can open /dev/hello0

# ./hello_test /dev/hello1
This is hello_open  //同樣調用的hello_open函數
can open /dev/hello1

# ./hello_test /dev/hello2
This is hello2_open ////調用的hello2_open函數
can open /dev/hello2

# ./hello_test /dev/hello3  //無法打開這個設備
can not open /dev/hello3

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章