在学习linux驱动开发中,我们可以将驱动编进内核,也可以编成模块,在编成模块时,我们希望模块加载时,设备文件可以自动创建,这样我们在开机脚本文件中进行模块加载,同时会创建设备文件。以后就直接可以操作这个设备了。
当然,有两种方法可以实现,比如led的驱动,可以将它注册为misc设备,这样也就不用手动创建设备文件了,但如果不使用misc设备,怎么办?
这就要使用第二种方法了,看下面的led驱动
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/gpio.h>
#define DEVICE_NAME "sxwleds" /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
#define LED_MAJOR 0 /* 主设备号 */
/* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
#define IOCTL_LED_ON 0
#define IOCTL_LED_OFF 1
unsigned int led_major=LED_MAJOR;
struct class *sxw_class;
/* 用来指定LED所用的GPIO引脚 */
static unsigned long led_table [] = {
S3C2410_GPB(5),
S3C2410_GPB(6),
S3C2410_GPB(7),
S3C2410_GPB(8),
};
/* 用来指定GPIO引脚的功能:输出 */
static unsigned int led_cfg_table [] = {
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
};
/* 应用程序对设备文件/dev/leds执行open(...)时,
* 就会调用s3c24xx_leds_open函数
*/
static int s3c24xx_leds_open(struct inode *inode, struct file *file)
{
int i;
for (i = 0; i < 4; i++) {
// 设置GPIO引脚的功能:本驱动中LED所涉及的GPIO引脚设为输出功能
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
}
return 0;
}
/* 应用程序对设备文件/dev/leds执行ioclt(...)时,
* 就会调用s3c24xx_leds_ioctl函数
*/
static int s3c24xx_leds_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
if (arg > 4) {
return -EINVAL;
}
switch(cmd) {
case IOCTL_LED_ON:
// 设置指定引脚的输出电平为0
s3c2410_gpio_setpin(led_table[arg], 0);
return 0;
case IOCTL_LED_OFF:
// 设置指定引脚的输出电平为1
s3c2410_gpio_setpin(led_table[arg], 1);
return 0;
default:
return -EINVAL;
}
}
/* 这个结构是字符设备驱动程序的核心
* 当应用程序操作设备文件时所调用的open、read、write等函数,
* 最终会调用这个结构中指定的对应函数
*/
static struct file_operations s3c24xx_leds_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = s3c24xx_leds_open,
.ioctl = s3c24xx_leds_ioctl,
};
/*
* 执行“insmod s3c24xx_leds.ko”命令时就会调用这个函数
*/
static int __init s3c24xx_leds_init(void)
{
int ret;
/* 注册字符设备驱动程序
* 参数为主设备号、设备名字、file_operations结构;
* 这样,主设备号就和具体的file_operations结构联系起来了,
* 操作主设备为LED_MAJOR的设备文件时,就会调用s3c24xx_leds_fops中的相关成员函数
* LED_MAJOR可以设为0,表示由内核自动分配主设备号
*/
ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops);
if (ret < 0) {
printk(DEVICE_NAME " can't register major number\n");
return ret;
}
led_major=ret;
printk(DEVICE_NAME " initialized---- MAJOR is %d\n",led_major);
sxw_class = class_create(THIS_MODULE,"sxw_class");
if(IS_ERR(sxw_class))
{
printk("Err: failed in creating sxw_class.\n");
return -1;
}
device_create(sxw_class,NULL,MKDEV(led_major,0),NULL,"sxwleds");
return 0;
}
/*
* 执行”rmmod s3c24xx_leds.ko”命令时就会调用这个函数
*/
static void __exit s3c24xx_leds_exit(void)
{
/* 卸载驱动程序 */
device_destroy(sxw_class,MKDEV(led_major,0));
class_destroy(sxw_class);
unregister_chrdev(led_major, DEVICE_NAME);
}
/* 这两行指定驱动程序的初始化函数和卸载函数 */
module_init(s3c24xx_leds_init);
module_exit(s3c24xx_leds_exit);
/* 描述驱动程序的一些信息,不是必须的 */
MODULE_AUTHOR("http://www.100ask.net"); // 驱动程序的作者
MODULE_DESCRIPTION("S3C2410/S3C2440 LED Driver"); // 一些描述信息
MODULE_LICENSE("GPL"); // 遵循的协议
上面的驱动程序中,加红的部分就是和自动创建设备文件相关的代码,device_create()这个函数用来创建一个名为“sxwleds”的字符设备。
之后,只要加载这个模块,就会在/dev/自动创建一个名为sxwleds的设备,为什么会自动创建,对于2.6的内核,是由用户空间的udev决定的。这方面的资料也很多,不再赘述。