linux操作系统的目标之一是向用户掩盖系统硬件设备的特殊性。驱动程序调用步骤:应用程序、库、内核、驱动、硬件。
linux嵌入式系统设备,例如:触摸屏、按键、 IIC总线、 LCD等是字符设备,他们特点是按字节流进行先后顺序读写操作设备的。
块设备可以随机访问设备内存的任意地址,硬盘、SD卡、NAND FLASH是块设备的代表。NAND Flash中的代码执行是通过将内容映射到RAM来实现的,这与直接从NOR Flash执行代码不同。记忆卡、体积小巧的U盘容量大。NAND Flash 的I/O接口并没有随机存取外部地址总线,它必须以区块性的方式进行读取。
NOR的特点是芯片内执行,应用程序可以直接在flash 闪存内运行,不必再把代码读到系统RAM中。NOR Flash需要很长的时间进行抹写,但是它提供完整的寻址与数据总线,并允许随机存取存储器上的任何区域,这使的它非常适合取代老式的ROM芯片。当时ROM芯片主要用来存储几乎不需更新的代码,例如电脑的BIOS或机上盒的固件。
网络设备指的是网卡一类使用socket套接字进行通信的设备。本文以字符设备为例讲述相关知识。
cat /proc/devices查看系统字符和块设备,在我们编写此类驱动insmod安装后,在该文件即可查看该驱动设备号信息。
Character devices:
1 mem
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
29 fb
99 ppdev
128 ptm
136 pts
162 raw
180 usb
188 ttyUSB
189 usb_device
202 cpu/msr
203 cpu/cpuid
226 drm
242 dimmctl
243 ndctl
244 aux
245 virtio-portsdev
246 hidraw
247 usbmon
248 bsg
249 hmm_device
250 watchdog
251 iio
252 rtc
253 dax
254 tpm
Block devices:
259 blkext
9 md
252 device-mapper
253 virtblk
254 mdp
Linux里面的misc杂项设备是主设备号为10的驱动设备,它的注册跟使用比较的简单,所以比较适用于功能简单的设备。随着 Linux字符设备驱动的不断增加,设备号变得越来越紧张,尤其是主设备号。
miscdevice是一个结构体,定义在文件 include/linux/miscdevice.h 中,内容如下:
struct miscdevice {
int minor; /* 子设备号 */
const char *name; /* 设备名字 */
const struct file_operations *fops; /* 设备操作集 */
struct list_head list;
struct device *parent;
struct device *this_device;
const struct attribute_group **groups;
const char *nodename;
umode_t mode;
};
使用 misc_register 函数向系统中注册一个 MISC 设备
相当于字符设备驱动中我们会使用如下几个函数完成设备创建过程
alloc_chrdev_region(); /* 申请设备号 */
cdev_init(); /* 初始化 cdev */
cdev_add(); /* 添加 cdev */
class_create(); /* 创建类 */
device_create(); /* 创建设备 */
调用 misc_deregister 函数来注销掉 MISC 设备
相当于注销设备驱动删除此前创建的 cdev、设备等等内容
cdev_del(); /* 删除 cdev */
unregister_chrdev_region(); /* 注销设备号 */
device_destroy(); /* 删除设备 */
class_destroy(); /* 删除类 */
ioctl是一种系统调用,用于在用户空间和内核空间之间传递控制命令。在Linux驱动程序中,ioctl通常用于实现设备驱动程序的控制接口。
驱动程序中的ioctl函数原型:
int ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
filp是一个指向文件结构体的指针。
cmd是要执行的命令。
arg是命令的参数。
驱动程序中的ioctl函数根据不同的cmd值,执行不同的操作。例如,可以使用ioctl函数来设置设备的工作模式、获取设备的状态等。
在用户空间,可以通过调用ioctl系统调用来与驱动程序交互。具体的使用方式和参数可以在ioctl的man页中查看。
驱动程序中的ioctl函数的实现通常需要处理文件结构体、命令和参数。可以根据具体的需求来实现不同的功能。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "demo.h"
MODULE_AUTHOR("fgj");
MODULE_LICENSE("Dual BSD/GPL");
struct simple_dev *simple_devices;
static unsigned char simple_inc=0;
static u8 demoBuffer[256];
int simple_open(struct inode *inode, struct file *filp)
{
struct simple_dev *dev;
if(simple_inc>0)return -ERESTARTSYS;
simple_inc++;
dev = container_of(inode->i_cdev, struct simple_dev, cdev);
filp->private_data = dev;
return 0;
}
int simple_release(struct inode *inode, struct file *filp)
{
simple_inc--;
return 0;
}
int simple_ioctl(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case COMMAND1:
memset(demoBuffer,0x31,256);
break;
case COMMAND2:
memset(demoBuffer,0x32,256);
break;
default:
return -EFAULT;
break;
}
return 0;
}
struct file_operations simple_fops = {
.owner = THIS_MODULE,
.ioctl = simple_ioctl,
.open = simple_open,
.release = simple_release,
};
/*******************************************************
MODULE ROUTINE
*******************************************************/
void simple_cleanup_module(void)
{
dev_t devno = MKDEV(simple_MAJOR, simple_MINOR);
if (simple_devices)
{
cdev_del(&simple_devices->cdev);
kfree(simple_devices);
}
unregister_chrdev_region(devno,1);
}
int simple_init_module(void)
{
int result;
dev_t dev = 0;
dev = MKDEV(simple_MAJOR, simple_MINOR);
result = register_chrdev_region(dev, 1, "DEMO");
if (result < 0)
{
printk(KERN_WARNING "DEMO: can't get major %d\n", simple_MAJOR);
return result;
}
simple_devices = kmalloc(sizeof(struct simple_dev), GFP_KERNEL);
if (!simple_devices)
{
result = -ENOMEM;
goto fail;
}
memset(simple_devices, 0, sizeof(struct simple_dev));
cdev_init(&simple_devices->cdev, &simple_fops);
simple_devices->cdev.owner = THIS_MODULE;
simple_devices->cdev.ops = &simple_fops;
result = cdev_add (&simple_devices->cdev, dev, 1);
if(result)
{
printk(KERN_NOTICE "Error %d adding DEMO\n", result);
goto fail;
}
return 0;
fail:
simple_cleanup_module();
return result;
}
module_init(simple_init_module);
module_exit(simple_cleanup_module);
#ifndef _simple_H_
#define _simple_H_
#include /* needed for the _IOW etc stuff used later */
/********************************************************
* Macros to help debugging
********************************************************/
#undef PDEBUG /* undef it, just in case */
#ifdef simple_DEBUG
#ifdef __KERNEL__
# define PDEBUG(fmt, args...) printk( KERN_DEBUG "DEMO: " fmt, ## args)
#else//usr space
# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
#endif
#else
# define PDEBUG(fmt, args...) /* not debugging: nothing */
#endif
#undef PDEBUGG
#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */
//设备号
#define simple_MAJOR 224
#define simple_MINOR 0
#define COMMAND1 1
#define COMMAND2 2
//设备结构
struct simple_dev
{
struct cdev cdev; /* Char device structure */
};
//函数申明
ssize_t simple_read(struct file *filp, char __user *buf, size_t count,
loff_t *f_pos);
ssize_t simple_write(struct file *filp, const char __user *buf, size_t count,
loff_t *f_pos);
loff_t simple_llseek(struct file *filp, loff_t off, int whence);
int simple_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg);
#endif /* _simple_H_ */