Linux设备驱动(二)-字符设备驱

字符设备驱动是学习Linux驱动开发的基础入门,所以应从此开始入手。这一节主要记录字符设备驱动的注册与卸载函数。
在内核Linux-2.6中的字符设备是用cdev结构体来描述的,注册和卸载驱动也是由cdev来完成的。
这次我们来把分解注释和讲解其中的用法。

cdev结构体:

1
2
3
4
5
6
7
8
struct cdev {
	struct kobject kobj;//定义kobject结构体
	struct module *owner;//所属模块,新注册的设备必须由"THIS_MODULE"这个值来指定。
	const struct file_operations *ops;//设备对应的操作函数集
	struct list_head list;//定义list结构体
	dev_t dev;//设备号
	unsigned int count;//需要分配的设备数目
};

cdev开发的过程应包括:注册设备号、定义cdev设备结构体、动态申请cdev设备内存(此步可选)、初始化cdev设备、注册cdev设备、卸载cdev设备和释放设备号。

注册设备号的方法有两种:动态和静态。动态适用与不了解系统设备号的情况下使用,将注册设备号的任务交给内核来完成,缺点却是一旦注册的设备跟之前有所不同可能导致应用与此设备的程序做出修改。而静态则是自己完成设备号的注册,缺点可能和已经注册的设备号冲突。无论是动态还是静态注册设备号,最后都使用同样的函数来释放已注册的设备号。

静态分配设备号:

1
2
3
4
int register_chrdev_region(dev_t form, unsigned int count, const char *name)
//from:申请使用的设备号
//count:申请使用设备号数目
//name:设备名(体现在/proc/devices)

动态分配设备号:

1
2
3
4
5
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned int count, const char *name)
//dev:分配到的设备号
//baseminor:起始次设备号
//count:需要分配的设备号数目
//name:设备名(体现在/proc/devices)

释放设备号:

1
2
3
void unregister_chrdev_region(dev_t from, unsigned int count)
//from:注销的起始设备号
//count:需要注销的设备号数目

设备号的注册一般在模块开始的时候”module_init()”中就应该完成,如果已经成功注册设备号而没有完成字符设备cdev函数的注册,则应该释放已经注册的设备号。而设备号的释放一般在”module_exit()”的中完成,并且应该卸载字符cdev后再释放设备号。

定义cdev结构体:

1
2
static struct cdev *p;
//p:定义cdev结构体的指针

动态申请cdev设备内存:

1
struct cdev *cdev_alloc(void)

初始化cdev设备:

1
2
3
void cdev_init(struct cdev *p,const struct file_operations *fops)
//p:待初始化的cdev结构
//fops:设备对应的操作函数集,也就是宣告的file_operations结构

注册cdev设备:

1
2
3
4
int cdev_add(struct cdev *p, dev_t dev, unsigned int count)
//p:待添加到内核的cdev设备结构
//dev:设备号
//count:添加的设备个数

注销cdev设备:

1
2
int cdev_del(struct cdev *p)
//p:要注销的字符设备结构

下面是s3c2440的led设备驱动例子,结合上面函数了解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <linux/cdev.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <mach/regs-gpio.h>
 
#define DEVICE_NAME "leds"
 
static struct cdev *leds_cdev;
static dev_t devno;
static unsigned int leds_table[]=
{
	S3C2410_GPE(14),
	S3C2410_GPE(15),
	S3C2410_GPH(9),
	S3C2410_GPH(10),
};
 
static unsigned int leds_cfg_table[]=
{
	S3C2410_GPIO_OUTPUT,
	S3C2410_GPIO_OUTPUT,
	S3C2410_GPIO_OUTPUT,
	S3C2410_GPIO_OUTPUT,
};
 
static	long leds_ioctl(
		struct file *file,
		unsigned int cmd,
		unsigned long arg)
{
	switch(cmd){
		case 0:
			if(arg > 4){
				return -EINVAL;}
			s3c2410_gpio_cfgpin(leds_table[arg],leds_cfg_table[arg]);
			s3c2410_gpio_setpin(leds_table[arg],!cmd);
		case 1:
			if(arg > 4){
				return -EINVAL;}
			s3c2410_gpio_cfgpin(leds_table[arg],leds_cfg_table[arg]);
			s3c2410_gpio_setpin(leds_table[arg],!cmd);
		return 0;
		default:
			return -EINVAL;
		}
}
 
static struct file_operations leds_fops=
{
	.owner = THIS_MODULE,
	.unlocked_ioctl = leds_ioctl,
};
 
static int __init leds_init(void)
{
	int ret;
	int err;
 
	ret= alloc_chrdev_region(&devno,0,1,"leds");
	if (ret<0)
	return ret;
 
	leds_cdev=cdev_alloc();
	cdev_init(leds_cdev,&leds_fops);
	err=cdev_add(leds_cdev,devno,1);
	if(err)
	{
		unregister_chrdev_region(devno,1);
		return -EFAULT;
	}
 
	printk(DEVICE_NAME "\tinitialized!\n");
	return 0;
}
 
static void __exit leds_exit(void)
{
	cdev_del(leds_cdev);
	unregister_chrdev_region(devno,1);
	printk(DEVICE_NAME "\tunloaded\n");
}
 
module_init(leds_init);
module_exit(leds_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yayi");

Linux设备驱动(一)-驱动基础概述

Linux系统的设备分为三种基本类型:字符设备(char device),块设备(block device)和网络设备(network device)三种。

  • 字符设备指存取时没有缓存必须以串行顺序依次进行访问的设备。
  • 块设备指以块为单位进行操作,读写操作都有缓存来支持,并且可以任意顺序进行访问的设备。
  • 网络设备在Linux里做专门的处理,主要对数据包的接收和发送而设计的。Linux的网络系统主要是基于BSD unix的socket机制。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递。系统里支持对发送数据和接收数据的缓存,提供流量控制机制,提供对多协议的支持。但内核与网络设备的通信方式和内核与字符设备、块设备之间的通信方式完全不同。

设备文件一般都在/dev/下面,用ll(ls -l)命令可以查看到相应的文件属性,Linux-2.6的内核已经使用了udev的设备文件系统用来代替devfs。

udev完全工作在用户态,利用设备加入或者移除时内核所发送的热插拔时间来工作。在热插拔时,设备的详细信息会由内核输出到位于/sys的sysfs文件系统。(摘选Linux设备驱动开发详解)

所有在/dev目录下的设备文件都是真实存在的设备(包括可以使用的虚拟设备),这是由于udev要求在成功加载驱动模块的时候就因建立对象的设备文件,这与devfs不同,devfs可以在被使用的时候创建,反之,udev在模块卸载的时候就应该删除对应的设备文件。详细的可以查看udev的主页(http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html)

Linux的驱动设备文件有设备号这个概念,由主设备号和次设备号构成。主设备号主要是区别不同类型设备,而次设备号是用来却别同类型设备中的不同设备。内核用dev_t类型来定义设备号。在2.6的内核里dev_t类型由32位构成。其中主设备号占有12位,次设备号占有20位。关于公共定义的设备号可以查看Linux内核目录中的”Documentation\devices.txt”文件,里面定义了一些参考。

下面几个函数用于设备号的使用:

1
2
3
MKDEV(int major, int minor);// 获取设备号
MAJOR(dev_t dev);//获取主设备号
MINOR(dev_t dev);//获取次设备号

使用例子1:

1
2
3
4
5
dev_t dev_no;//设备号
int major_no,minor_no;//major_no为主设备号变量,minor_no为次设备号变量
dev_no=MKDEV(180,96);
major_no=MAJOR(dev_no);//获取设备号中的主设备号
monir_no=MONIR(dev_no);//获取设备号中的次设备号

手工创建设备文件

View Code SHELL
1
2
3
4
5
mknod filename type major minor
filename:设备文件名
type:设备文件类型(c:字符设备文件,b:块设备文件)
major:主设备号
minor:次设备号

使用例子2:

View Code SHELL
1
mknod leds c 180 96

MCU 8051 IDE

MCU-8051-IDE+SDCC=Linux下的单片机开发环境.

如果再加入gSTC-IPS那就已经可以离开WIN开发STC单片机了.

MCU-8051-IDE:mcu8051ide.sourceforge.net/

gSTC-IPS:gstcisp.sourceforge.net/

SDCC:sdcc.sourceforge.net/

MCU 8051 IDE是一个非常不错的跨平台IDE,

支持LINUX、WINDOWS、MAC系统,

只需要解压便可以使用,

但是需要有TCL/TK这些支持,需要自己先安装好.

看下各种模拟器:

然后加入gSTC-IPS的下载器.

MCU 8051 IDE我自己已经用了4天时间来初步翻译了下,

所以大家下载的是英文版.

由于翻译质量不高,

错误也可能很多,

所以我不建议使用,

也不打算推送给MCU 8051 IDE.

如果确实需要的可以留下email或者email to me .

在使用过程如果发现错误请email给我.

First Diy MCU

第一次接触单片机,

而且是散件,需要自己焊接那种,

在焊接单片机之前焊接过两个收音机和一个门铃,

不过基本都成为死亡的小白鼠.

抱着搏一搏的态度还是开始了焊接单片机.

足足用了两天,

第一天为单片机,第二天为模块版.

虽然焊接称不上好看,

但是对我自己来说已经非常不错了.

而且,焊接技术也大大的进步了.

从门铃到模块版这段进步付出了不小的代价.

一个洛铁的头已经宣布残废.自己也烫伤了两次.

虽然这样,还是感觉到很欣慰的.

最起码成功了.

Fedora 安装 Mplayer

前提工作

1.使用svn下载 mplayer

svn checkout svn://svn.mplayerhq.hu/mplayer/trunk mplayer
如果无法再这里下载不要泄气.这很可能是 GWF的问题.
可以转向http://www.mplayerhq.hu/design7/dload.html这 里下载”Subversion snapshot”.

2.下载 解码器
到这里
http://www.mplayerhq.hu/MPlayer/releases/codecs/下载解码器
Yayi 选择比较新的解码器all-20100303.tar.bz2这个使用.

3.到http://www.mplayerhq.hu/design7/dload.html这里的Skins处下载自己喜欢的主题

4.确认自己已经安装了gtk2- devel,如果没有请自行用yum安装.

安装Mplayer
1.切换到root用户
解压all-20100303.tar.bz2

View Code SHELL
1
tar jxvf all-20100303.tar.bz2


2.把里面的所有文件复制到你要存放的位置.
例 如笔者希望存放到”/usr/share/mplayer/lib“这里

View Code SHELL
1
cp *  /usr/share/mplayer/lib

3. 进入mplayer目录.然后执行下列代码.

View Code SHELL
1
./configure --prefix=/usr/share/mplayer  --enable-gui --enable-freetype --codecsdir=/usr/share/mplayer/lib  --language=zh_CN

–prefix=/usr/share/mplayer 指定安装位置
–enable-gui 启用图形
–enable-freetype 开启字体的选择
–codecsdir=/usr/share/mplayer/lib 这里就是你刚刚把加码器存放的位置.
–language=zh_CN 设置语言为中文.

4.make;make install进行安装

5.把你下载的主题解压.
修改主题目录名为default.
然后复制到安装目录的”share/mplayer/skins“下

6.这样就安装完毕了.
只需要把安装目录下bin目录下的gmplayer复制到/usr/bin里面便可以使用mplayer了.

View Code SHELL
1
cp gmplayer /usr/bin/

使用时为:

View Code SHELL
1
gmplayer 文件名

笔者这里播放的是一首MP3所有没有视频.

7.建立桌面快捷方式
桌面点击右键选择”创建启动器”
然后像下图这样填写便可以.

Fedora 安装 skyeye

前提工作

使用yum 安装以前软件

ncurses
ncurses-devel
glibc

glibc-devel

libXpm
libXpm-devel

texinfo
xterm
xorg-x11-fonts-misc

安装完”xorg-x11-fonts-misc“后,使用root权限运行下面程序:

View Code SHELL
1
fc-cache -fv

下载skyeye

http://sourceforge.net/projects/skyeye/files/

记住把skyeye,skyeye-testsuits,skyeye-documentation3个都下载下来,

skyeye是源码.

skyeye-testsuits是测试用的.

skyeye-documentation为skyeye文档,

安装skyeye

View Code SHELL
1
2
3
4
5
./configure --prefix=你希望安转的地方(当然这个选项可以不带让其默认安装)
make lib
make
make install_lib
make install

安装后查看下自己指定的目录.如下图:

测试skyeye

把之前下载好的skyeye-testsuits解压放在skyeye目录的testsuits下.

首先我们来测试一下s3c2440

进入测试目录:

View Code SHELL
1
~/skyeye/testsuite/skyeye-testsuite/linux/s3c2440/2.6.30.5

然后在终端运行以下skyeye:

View Code SHELL
1
~/skyeye/bin/skyeye -e vmlinux

然后按照下图输入:”start

接着按照下图输入:”run

这样就证明我们成功了.

为了方便使用,建议把bin下面的skyeye_main.py链接ln到/usr/bin/目录下面

官方网站为:skyeye.org