linux驱动学习之字符设备驱动模板

今天整理了一下字符设备驱动的模板,工程分开3个文件.

main.c        驱动加载初始化工作。

loadmod.c  提供安装和卸载驱动的函数。

fileops.c     文件操作函数。


Makefile

#KERNELDIR = /home/fontlose/board/tx2416/kernelsom2416
KERNELDIR = /usr/src/kernels/2.6.35.13-92.fc14.i686/
PWD := $(shell pwd)
#CC    = arm-linux-gcc
moddev-objs=main.o fileops.o loadmod.o
obj-m+=moddev.o 
modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

.PHONY: modules clean 

main.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/init.h>
#include "loadmod.h"
#include "fileops.h"
MODULE_AUTHOR("my name");
MODULE_LICENSE("Dual BSD/GPL");
#define MAJORNUM  0
#define MINJORNUM 60 

struct file_operations ops=
{
   .owner=THIS_MODULE,
   .open=fileops_open,
   .write=fileops_write,
   .read=fileops_read,
   .release=fileops_release
};

static int template_init(void)
{
 if ( install_chardev(MAJORNUM,MINJORNUM,4,"moduledev","modclass",&ops))    
     return -1;
  if(fileops_init())
  {
    uninstall_chardev();  
    return -1;
  }
  return 0;
}
static void template_exit(void)
{
  uninstall_chardev();
  fileops_destroy();
  printk(KERN_ALERT "goodbye\n");
}
module_init(template_init);
module_exit(template_exit);


loadmod.h

#ifndef   __LOADMOD_H__
#define  __LOADMOD_H__
#include <linux/fs.h>
  extern unsigned char install_chardev(int major_i,int minor_i,unsigned char count,char*devname,char*classname,struct file_operations *ops);
  extern int uninstall_chardev(void);
#endif

loadmod.c

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/err.h>
#include <linux/cdev.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/string.h>

static struct cdev  cdev_install;         //
static struct class *class_install;       //
static int  major_install,minor_install;  //
static unsigned char count_install;       //  

/**
 * install_chardev  字符设备注册函数
 * @major_i 主设备号
 * @minor_i 次设备号
 * @count   设备个数
 * @devname 设备名+注册的设备名自动在/dev下生成devname+次设备号的文件
 * @classname 设备类名
 * @ops     文件操作数据结构
 */
unsigned char install_chardev(int major_i,int minor_i,unsigned char count,char*devname,char*classname,struct file_operations *ops)
{
  //step 1:设备号的注册
  int i; dev_t dev; 
  if(major_i==0) //动态申请一个主设备号
  {
      int res=alloc_chrdev_region(&dev,minor_i,count,devname);
	  if(res){
	    printk(KERN_ALERT "alloc chrdev region fail!\n");
		return -1;
      }	
	  major_i=MAJOR(dev);
  }
  else
  {
      int res;
	  dev=MKDEV(major_i,minor_i);
	  res=register_chrdev_region(dev,count,devname);
	  if(res){
	    printk(KERN_ALERT "register chrdev region fail!\n");
		return -1;
      }		  
  }
  major_install=major_i;
  minor_install=minor_i;
  count_install=count;

  //step 2:注册设备
  cdev_install.count=0;
  if(ops!=0)
  {
    cdev_init(&cdev_install,ops);
    cdev_install.owner=THIS_MODULE;
    cdev_install.ops  =ops;
    if( cdev_add(&cdev_install,dev,count_install) )
    {
	  unregister_chrdev_region(dev,count_install); 
	  printk(KERN_ALERT "cdev add fail!\n");
	  return -1;
    }  
  }
  //step 3:创建设备节点 注意在linux 2.6
  //低版本使用class_device_create和class_device_destroy,高版本使用device_create和device_destroy
  class_install=class_create(THIS_MODULE,classname);
  if(IS_ERR(class_install))
  {
	if(cdev_install.count>0) cdev_del(&cdev_install);  
	unregister_chrdev_region(dev,count_install); 
	printk(KERN_ALERT "class creat fail!\n");	  
    return -1;
  }
  for(i=0;i<count;i++)
  {
    device_create(class_install,NULL,MKDEV(major_install,minor_install+i),NULL,"%s%d",devname,minor_install+i);
  }
  printk(KERN_ALERT "device initial success major:%d minor:%d!\n",major_install,minor_install);	  
  return 0;
}


/**
 * uninstall_chardev 卸载字符设备
 */
int uninstall_chardev(void)
{
	int i;
    if(cdev_install.count >0)
	   cdev_del(&cdev_install);  
	unregister_chrdev_region(MKDEV(major_install,minor_install),count_install);
	for(i=0;i<count_install;i++)
	{
	  device_destroy(class_install,MKDEV(major_install,minor_install+i)); 
	}
	class_destroy(class_install);
	return 0;
}

fileops.h

#ifndef __FILEOPS_H__
#define __FILEOPS_H__
#include <linux/fs.h>
extern int  fileops_init(void);
extern void fileops_destroy(void);
extern int fileops_open(struct inode *inode, struct file *filp);
extern int fileops_release(struct inode *inode,struct file *filp);
extern ssize_t fileops_read(struct file *filp, char __user *buff, size_t count, loff_t *offp);
extern ssize_t fileops_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp);
extern int fileops_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
#endif


fileops.c

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kdev_t.h>
#include <linux/err.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include "fileops.h"
#include <asm/uaccess.h>

/**
 * 初始化 在加载时候调用
 */
int fileops_init(void)
{
  return 0;
}

/**
 *释放函数 在模块卸载时候调用
 */
void fileops_destroy(void)
{


}

/**
 * 文件打开 驱动初始化为后续操作做准备,如果不实现默认打开成功 
 * 初始化设备,如有需要则初始化filp->private_data
 *
 * @inode 文件的索引节点,它用来存放档案及目录的基本信息,可取inode结构内
 *        的次编号来确定具体哪个设备
 * @filp  
 */ 
int fileops_open(struct inode *inode, struct file *filp)
{
  printk(KERN_ALERT "fileops_open\n");
  return 0;   
}

/**
 * 文件关闭 释放open申请的内存 关闭设备
 */
int fileops_release(struct inode *inode,struct file *filp)
{
  printk(KERN_ALERT "fileops_release\n");
  return 0;
}

/**
 *读设备
 *@filp  一般操作private_data数据
 *@buff  用户空间的缓存,用于存放读取的数据
 *@count 用户想要读取的数据大小
 *@offp  用户正在存取的文件位置
 *返回实际读出大小 负数表示出错  
 */
ssize_t fileops_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
  /*每次读完更新offp*/
  printk(KERN_ALERT "fileops_read\n");
  return 0;
}

/**
 *写设备
 *@filp  一般操作private_data数据
 *@buff  用户空间的缓存,用于存放要写的数据
 *@count 用户想要写入的数据大小
 *@offp  用户正在存取的文件位置
 *返回实际写入大小 负数表示出错
 */
ssize_t fileops_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp)
{
  /*这里系统调用会一直写 直到写够count,如返回小于count的数,会再次调用此方法直到返回错误或写够count*/
  printk(KERN_ALERT "fileops_write\n");
  return count;
}

/**
 *fileops_ioctl ioctl 系统调用提供了发出设备特定命令的方法
 *@inode
 *@filp
 *@cmd
 *@arg
 */
int fileops_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
  printk(KERN_ALERT "fileops_ioctl\n");
  return 0;
}

测试结果

[root@localhost module]# make
make -C /usr/src/kernels/2.6.35.13-92.fc14.i686/ M=/home/fontlose/board/tx2416/rootfs/usr/ldds/module modules
make[1]: Entering directory `/usr/src/kernels/2.6.35.13-92.fc14.i686'
  CC [M]  /home/fontlose/board/tx2416/rootfs/usr/ldds/module/main.o
  CC [M]  /home/fontlose/board/tx2416/rootfs/usr/ldds/module/fileops.o
  CC [M]  /home/fontlose/board/tx2416/rootfs/usr/ldds/module/loadmod.o
  LD [M]  /home/fontlose/board/tx2416/rootfs/usr/ldds/module/moddev.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/fontlose/board/tx2416/rootfs/usr/ldds/module/moddev.mod.o
  LD [M]  /home/fontlose/board/tx2416/rootfs/usr/ldds/module/moddev.ko
make[1]: Leaving directory `/usr/src/kernels/2.6.35.13-92.fc14.i686'


[root@localhost module]# dmesg|tail -2
[31864.045477] device initial success major:247 minor:60!
[31872.877461] goodbye

[root@localhost module]# ls /dev/moduledev6*
/dev/moduledev60  /dev/moduledev61  /dev/moduledev62  /dev/moduledev63

[root@localhost module]# ls /sys/class/modclass/
moduledev60  moduledev61  moduledev62  moduledev63

[root@localhost module]# echo 1234 > /dev/moduledev60 
[root@localhost module]# dmesg|tail -3
[32022.480159] fileops_open
[32022.480171] fileops_write
[32022.480174] fileops_release








你可能感兴趣的:(linux驱动学习之字符设备驱动模板)