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

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

(0)

相关推荐

  • linux块设备读写流程详解

    在学习块设备原理的时候,我最关系块设备的数据流程,从应用程序调用Read或者Write开始,数据在内核中到底是如何流通.处理的呢?然后又如何抵达具体的物理设备的呢?下面对一个带Cache功能的块设备数 ...

  • Linux系统的硬件设备驱动的底层结构讲解

    什么是驱动?最通俗的解释就是“驱使硬件设备行动” 作用?设备驱动与底层硬件直接打交道,按照硬件设备的具体工作方式读写设备寄存器,完成设备的轮询.中断处理.DMA通信,进行物理内存向虚拟内存的映射,最终 ...

  • 如何获得Linux系统的内置模块和设备驱动列表

    提问:我想要知道Linux系统中内核内置的模块,以及每个模块有哪些参数.有什么方法可以得到内置模块和设备驱动的列表,以及它们的详细信息呢? 现代Linux内核正在随着时间变化而迅速增长,以支持大量的硬 ...

  • 深入解析Win7的设备驱动管理

    是否能够对硬件提供良好的支持,是Windows7面临的一个严峻考验,同时也是用户是否选择Windows 7的一个重要指标.所谓的硬件支持,说到底就是设备的驱动问题.Windows7在硬件的驱动方面有哪 ...

  • 让Windows Server 2008设备驱动安装图文教程

    安装设备驱动程序原本是一件非常简单的事情,很多驱动程序在安装的时候我们只要不停单击“下一步”按钮,就能让驱动程序顺利地在对应计算机系统“落户”;不过,当身边的计算机系统升级为Windows Serve ...

  • win8系统在新界面安装与使用硬件设备驱动详细图解

    Windows 8在功能上的最大亮点就是新界面及新界面应用,硬件设备驱动在新界面中也能够顺利的安装和使用。具体方法如下: 第一步:鼠标移动到右上或者右下角,在侧边栏单击“设置”。 第二步:在“设置”中 ...

  • 360随身wifi驱动异常怎么办?360随身wifi usb设备驱动异常解决方法

    360随身wifi驱动异常怎么办?下文将告诉大家360随身wifi usb设备驱动异常解决方法,有网友表示360随身wifi一直提示usb异常无法安装,这是怎么回事?可以解决吗? 法一、到官网下载最新 ...

  • win8无线网卡搜索不到信号但无线设备驱动正常的解决方法

    预装Win8中文版系统,无线设备驱动正常,但搜索不到信号。飞行模式确认为关闭。如下图所示: 分析原因:Win8系统设置里新增加了对无线网卡的控制,需要启用才能连接无线网络。 解决方案: 1. 打开超级 ...

  • 驱动人生如何解决USB设备驱动

    图1 驱动人生2009正式版安装界面 插入语:小编为了让大家更直观的看到,驱动人生秒杀USB设备驱动的全过程,不得己用驱动人生卸载了自己的USB设备驱动,可怜小编一篇苦心,希望对大家有所帮助。 图2 ...