Linux 设备驱动 ====> 字符驱动

开始从头学起linux 设备驱动,当然是先从字符驱动看起。

下面仿照着书上的例子,写了一个misc 字符驱动。

root@jay-LJ:/home/jay/globalmem# tree globalmem/ globalmem/ ├── globalmem.c └── Makefile
首先咱来看下Makefile, 其实这东西都一个模子,

KVERS = $(shell uname -r) obj-m += globalmem.o build: kernel_modules kernel_modules: make -C /lib/modules/$(KVERS)/build/ M=$(CURDIR) modules clean: make -C /lib/modules/$(KVERS)/build/ M=$(CURDIR) clean
然后是我们的驱动文件globalmem.c,我们从init函数看起

int globalmem_init(void) { int result; globalmem_devp = kmalloc(sizeof(struct globalmem_dev) ,GFP_KERNEL); if(!globalmem_devp) { result = -ENOMEM; goto fail_malloc; } memset(globalmem_devp, 0, sizeof(struct globalmem_dev)); globalmem_devp->mdev = mdev_struct; result = misc_register(&(globalmem_devp->mdev)); if(result<0) return result; else return 0; fail_malloc: return result; }
首先是给我们的全局指针变量分配内存

globalmem_devp = kmalloc(sizeof(struct globalmem_dev) ,GFP_KERNEL); if(!globalmem_devp) { result = -ENOMEM; goto fail_malloc; } memset(globalmem_devp, 0, sizeof(struct globalmem_dev));
看下这个指针的定义

struct globalmem_dev { struct miscdevice mdev; unsigned char mem[GLOBALMEM_SIZE]; }; struct globalmem_dev *globalmem_devp;
globalmem_dev结构体中内嵌了一个miscdevice结构体,这个结构体就是描述我们的misc字符驱动的结构体,若想注册一个misc 字符设备就必须包含有这样一个结构体,

struct miscdevice { int minor; const char *name; const struct file_operations *fops; struct list_head list; struct device *parent; struct device *this_device; };
这个结构体描述了这个驱动的信息,包含次设备号, 文件操作结构体,驱动名称等。

内存分配结束之后是调用misc_register来注册我们的misc驱动,使用misc字符驱动相对于标准的字符驱动写起来简单。

然后是exit函数,跟init相反:

void globalmem_exit(void) { misc_deregister(&(globalmem_devp->mdev)); if(globalmem_devp) kfree(globalmem_devp); } module_init(globalmem_init); module_exit(globalmem_exit);
千万别忘了最后要释放我们分配的内存。

然后使我们的miscdevice和fops结构体

static const struct file_operations globalmem_fops = { .owner = THIS_MODULE, .open = globalmem_open, .release = globalmem_release, .unlocked_ioctl = globalmem_ioctl, .read = globalmem_read, .write = globalmem_write, }; static struct miscdevice mdev_struct = { .minor = MISC_DYNAMIC_MINOR, .name = "globalmem", .fops = &globalmem_fops, };
这里主要是这个file_operations接头体的定义,这里主要是把这个结构体中我们想要实现的读,写等方法与对应的回调函数挂钩起来。

然后就是我们的读写函数了

int globalmem_open(struct inode *inode, struct file *filp) { printk(KERN_INFO "globalmem open!\n"); filp->private_data = globalmem_devp; return 0; } int globalmem_release(struct inode *inode ,struct file *filp) { printk(KERN_INFO "globalmem release!\n"); return 0; } static int globalmem_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { struct globalmem_dev *dev = filp->private_data; //get global data pointer switch(cmd) { case MEM_CLEAR: memset(dev->mem, 0, GLOBALMEM_SIZE); printk(KERN_INFO "clear globalmem!\n");\ break; default: return -EINVAL; } return 0; } static ssize_t globalmem_read(struct file *filp, char __user *buf,size_t size, loff_t *ppos) { unsigned long p = *ppos; unsigned int count = size; int ret = 0; struct globalmem_dev *dev = filp->private_data; //get global data pointer if(p>=GLOBALMEM_SIZE) return 0; if(count > GLOBALMEM_SIZE-p) count = GLOBALMEM_SIZE-p; if(copy_to_user(buf, (void *)(dev->mem + p), count)) ret = -EFAULT; else { *ppos += count; ret = count; printk(KERN_INFO "read %u bytes(s) from %lu\n",count,p); } return ret; } static ssize_t globalmem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) { unsigned long p = *ppos; unsigned int count = size; int ret = 0; struct globalmem_dev *dev = filp->private_data; //get global data pointer if(p >= GLOBALMEM_SIZE) return 0; if(count > GLOBALMEM_SIZE - p) count = GLOBALMEM_SIZE - p; if(copy_from_user(dev->mem+p, buf, count)) { printk(KERN_INFO "copy from user error!!\n"); ret = -EFAULT; } else { *ppos += count; ret = count; printk(KERN_INFO "written %u bytes(s) from %lu\n",count,p); } return ret; }
我这里实现了open release write read四个方法,open方法很简单,把我们的自己定义的全局的结构体指针保存到我们驱动中的私有指针,这样的话,在我们别的方法中就可以直接用这个私有指针来获取我们的全局结构体指针,

filp->private_data = globalmem_devp;
保存指针,

struct globalmem_dev *dev = filp->private_data; //get global data p
获取指针。

在我们的read/write中是与用户空间进行数据处理的过程,主要是调用了copy_from_user和copy_to_user这2个函数实现的,实现了从用户态到内核态的数据传递。

在应用层使用read/write来进行系统调用call到我们这边的read/write回调函数。

============================================================

接下来我们来测试一下我们的驱动,

编译

root@jay-LJ:/home/jay/globalmem/globalmem# make make -C /lib/modules/2.6.35-22-generic/build/ M=/home/jay/globalmem/globalmem modules make[1]: Entering directory `/usr/src/linux-headers-2.6.35-22-generic' CC [M] /home/jay/globalmem/globalmem/globalmem.o /home/jay/globalmem/globalmem/globalmem.c:110: warning: initialization from incompatible pointer type Building modules, stage 2. MODPOST 1 modules CC /home/jay/globalmem/globalmem/globalmem.mod.o LD [M] /home/jay/globalmem/globalmem/globalmem.ko make[1]: Leaving directory `/usr/src/linux-headers-2.6.35-22-generic'
加载

insmod globalmem.ko

查看驱动

root@jay-LJ:/home/jay/globalmem/globalmem# ls -l /dev/globalmem crw------- 1 root root 10, 53 2012-03-27 21:11 /dev/globalmem
进行读写测试

root@jay-LJ:/dev# chmod 666 globalmem root@jay-LJ:/dev# echo "Hello globalmem driver" > globalmem root@jay-LJ:/dev# cat globalmem Hello globalmem driver root@jay-LJ:/dev#
当然了,我们也可以自己写一个测试的应用程序来测试我们的驱动。

结束。

=========================================================

mail & MSN :zhangjie201412@live.com

=========================================================

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux字符设备驱动实验是指在Linux操作系统中编写和测试字符设备驱动程序的过程。字符设备驱动程序负责与字符设备进行交互,包括输入输出数据、控制设备和处理设备的状态等。 在进行Linux字符设备驱动实验之前,首先需要了解字符设备字符设备驱动的基本概念及其工作原理。字符设备是指以字符为单位进行输入输出的设备,如串口、打印机等。字符设备驱动是指将操作系统与字符设备进行交互的程序。 在实验中,我们通常需要编写一个字符设备驱动程序,包括初始化设备、读写数据、控制设备等功能。首先,我们需要定义字符设备驱动的数据结构,包括设备号、驱动程序打开、关闭等函数的实现。然后,我们需要实现字符设备驱动的读写函数来实现数据的输入输出。最后,我们可以进行一些附加功能的实现,如控制设备的状态、处理中断等。 在实验过程中,我们需要使用Linux内核提供的字符设备接口来进行字符设备驱动的编写和测试。可以使用一些工具和命令来加载和测试字符设备驱动程序,如insmod、rmmod等。通过这些工具和命令,我们可以加载和卸载字符设备驱动程序,并在用户空间进行数据的读写操作,来测试字符设备驱动的功能和性能。 Linux字符设备驱动实验可以帮助我们深入了解字符设备字符设备驱动的工作原理,并学习Linux内核的开发和调试技术。通过实验,我们可以更好地理解操作系统和驱动程序之间的关系,提高我们在Linux系统开发和嵌入式系统开发中的能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值