Linux系统下的驱动程序开发

来源 :科学与财富 | 被引量 : 0次 | 上传用户:hukuikui
下载到本地 , 更方便阅读
声明 : 本文档内容版权归属内容提供方 , 如果您对本文有版权争议 , 可与客服联系进行内容授权或下架
论文部分内容阅读
  摘 要: 本文以PC-6325A板卡Linux系统下的驱动开发为例介绍了PC-6325A板卡信息及工作原理,linux内核编译加载、常用函数方法实现等基本编程技术。可为Linux系统下驱动程序开发人员提供一定参考。
  关键词: Linux;驱动程序
  1 引言
  由于Linux系统具有运行安全稳定、功能强大、获取方便等众多优点,逐渐被众多开发人员所使用。但市场上Linux发行版本多,很多硬件设备缺少对应版本的驱动程序,需要自行开发。
  本文以PC-6325A板卡开发为例,介绍了linux内核编译加载、常用函数方法实现等基本编程技术,在后续驱动程序开发中可以此为参考。
  2 硬件设备分析
  PC-6325A模入接口卡适用于具有ISA总线的PC系列微机,具有很好的兼容性,CPU从目前广泛使用的64位处理器到早期的16为处理器均可使用,操作系统也适用于MS-DOS、Windows系列、Unix等多种操作系统以及专业数据采集分析系统labVIEW等软件环境。以下对PC-6325A板卡进行详细介绍。
  2.1 工作原理
  PC-6325A板卡主要由模拟多路开关电路、放大器电路、模数转换电路、接口控制逻辑电路、光电隔离电路及DC/DC电源电路组成。
  2.1.1 模拟多路开关电路
  模拟多路开关由4片8选1模拟开关芯片等组成,通过KJ1和KJ2跨接插座可以选择32路单端或16路双端输入方式,并将选中的信号送入差分放大器处理。
  2.1.2 模数转换电路
  PC-6325A卡选用新一代A/D器件ADS7808作为模数转换器件。ADS7808内部自带采保和精密基准电源。A/D转换可以由程序启动,也可由外部触发信号启动。A/D转换结束标志可以由程序查询检出,也可通过中断方式通知CPU处理。
  2.1.3 接口控制逻辑电路及光隔电路
  接口控制逻辑电路用来产生与各种操作有关的控制信号。光隔电路采用6N137高速光耦对系统总线与模拟信号之间进行光电隔离,以避免相互间的干扰。
  2.1.4 DC/DC电源电路
  DC/DC电源电路有电源模块及相关的滤波元件组成。该电源模块的输入电压为+5V,输出电压为与原边隔离的±15V和+5V,原付边之间隔离电压可达1500V。
  3 驱动程序设计
  3.1 编译内核
  设备驱动属于linux内核的部分在编写Linux设备驱动前需要对Linux操作系统内核进行编译。本次PC-6325板卡驱动开发调试采用2.6.23版本内核。
  编译步骤如下:
  1.下载并解压Linux内核一般内核源码放在/usr/src目录下。
  2.清除从前编译内核时残留的.o文件和不必要的关联:
  cd /usr/src/linux
  make mrproper
  確保源代码目录下没有不正确的.o文件和文件依赖关系,执行该命令后,内核选项会回到默认的状态下。如果为下载的内核源码,且为第一次编译可跳过该步骤。
  3.配置内核,修改相关参数:
  图形界面下,make xconfig;
  字符界面下, make menuconfig;
  在内核配置菜单中正确设置内核选项保存退出。
  4.正确设置关联文件:
  make dep
  根据上一步所选择的选项,建立文件的依赖关系。
  5.编译内核:
  对于大内核,make bzlmage
  对于小内核,mkae zlmage
  6.编译模块:
  make modules
  编译可加载模块(即内核选项中选择为M的选项)。
  7.安装模块:
  make modules_install
  将编译好的modules拷贝到/lib/modules下(该步骤需要管理员权限)。
  3.2 编写驱动程序
  内核编译完成后就可以进行驱动程序代码编写了,但在此之前需要了解一下Linux系统的一个基本概念,内核空间和用户空间。模块运行在内核空间用于扩展内核功能,应用程序运行在用户空间。内核空间与用户空间可理解为两种运行模式,有着不同的优先级及自己的内存映射。相应的在代码编写中也会有内核空间编程及用户空间编程的区别。本文将以PC-6325A板卡驱动开发中的具体代码为例对驱动程序中内核空间及用户空间较为常用的函数方法进行详细介绍。
  3.2.1 初始化和关闭
  1.初始化代码如下:
  static int __init pc6325_init (void){
  register_chrdev(MAJOR_NUM, "pc6325", &pc6325_fops);
  gPc6325Dev = kmalloc(sizeof(struct pc6325_dev), GFP_KERNEL);
  init_MUTEX(&gPc6325Dev->sem); /*初始化信号量*/
  return 0;
  }
  module_init(pc6325_init);
  初始化函数声明为static,该函数仅在初始化期间使用。模块装载之后,模块装载器将初始化函数丢掉,并释放函数所占用的内存。
  module_init用于说明内核初始化函数的位置。如果没有这个定义,初始化函数将无法被调用。   2.清除函数代码如下:
  static void __exit pc6325_exit (void){
  unregister_chrdev(MAJOR_NUM, "pc6325");
  }
  module_exit(pc6325_exit);
  清除函数在模块移除前注销接口并返回系统中的所有资源。清除函数无返回值,声明为void清除函数用于模块卸载,在模块卸载或者系统关闭时调用。
  3.2.2 数据读取与发送
  1.读取数据代码如下
  static ssize_t pc6325_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 pc6325_dev *dev = filp->private_data; /*获得设备结构体指针*/
  if (down_interruptible(&dev->sem)) /* 获得信号量 */
  {
  return - ERESTARTSYS;
  }
  dev->mem[0]=inb(dev->address+p);
  copy_to_user(buf, &(dev->mem[0]), count);
  up(&dev->sem);
  return ret;
  }
  该函数用来从设备读取数据,函数指针被赋予NULL值时,将导致reed系统调用出错并返回-EINVAL(非法参数)。函数返回非负值表示成功读取的字节数。对于该方法,参数filp是文件指针,参数count是请求传输的数据长度。参数buff是用户空间的指针,指向用户空间的缓冲区,保存要写入的数据,或者是一个存放新读入数据的空缓冲区。最后的offp是一个指向长偏移量类型的对象指针,用于指明用户在文件中进行存取操作的位置。
  2.发送数据函数如下:
  static ssize_t pc6325_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;
  unsigned char vData=0;
  struct pc6325_dev *dev = filp->private_data; /* 获得设备结构体指针 */
  if( count<3 ){
  return;
  }
  if (down_interruptible(&dev->sem)) /* 获得信号量 */
  {
  return - ERESTARTSYS;
  }
  copy_from_user(dev->mem+p,buf,count);
  if( count==3 && dev->mem[0]==0xED ){
  dev->address=dev->mem[1]*0x100+dev->mem[2];
  pc6325_set_base_address(dev->address);
  }else if( count==3 && dev->mem[0]==0xCD ){
  dev->offset=dev->mem[1];
  vData=dev->mem[2];
  outb(vData,dev->address+dev->offset);
  }
  up(&dev->sem); /* 釋放信号量 */
  return ret;
  }
  向设备发送数据。如果返回值非负,则表示成功写入的字节数。其用法及参数与read方法一致,在此不再赘述。
  3.2.3 open和release方法
  1.open方法:
  int pc6325_open(struct inode *inode, struct file *filp)
  {
  filp->private_data = gPc6325Dev;
  return 0;
  }
  Open方法提供给驱动程序初始化的能力,从而为以后的操作完成初始化的准备。在多数驱动程序中,open完成如下工作:
  a)检查设备特定的错误;
  b)如果设备是首次打开,则对其进行初始化;
  c)必要时,更新f_op指针;
  d)非配并填写置于filp->private_data里的数据结构[1]。
  2.release方法:
  int pc6325_release(struct inode *inode, struct file *filp)
  {
  struct pc6325_dev *dev = filp->private_data;
  pc6325_release_base_address(dev->address);   //printk(KERN_ALERT "address release");
  return 0;
  }
  release方法的作用于open正好相反。该方法通常完成以下任务。
  a)释放有open分配的、保存在filp->private_data中的所有内容;
  b)在最后一次关闭操作时关闭设备。
  3.2.4 file_operations结构
  代码如下:
  struct file_operations pc6325_fops={
  read: pc6325_read,
  write: pc6325_write,
  open: pc6325_open,
  release: pc6325_release,
  owner: THIS_MODULE,
  };
  file_operations结构用于建立驱动程序与设备编号之间的连接。主要用于实现系统调用。结构中的每一个字段都指向驱动程序中特定操作的函数,对于不支持的操作,对应的字段可设置为NULL值。
  3.2.5 模块加载与卸载
  1. 模块加载
  # Create the device nodes (up to 10 by default)
  echo -n "Creating device nodes........... "
  rm -f ${path}/${name}*
  mknod ${path}/${name} c $major 226
  #mknod ${path}/${name} c 226 0
  # Create additional nodes for non-service driver
  if [ "${bServiceDriver}" == "0" ]; then
  mknod ${path}/${name}-0 c $major 000
  mknod ${path}/${name}-1 c $major 001
  mknod ${path}/${name}-2 c $major 002
  mknod ${path}/${name}-3 c $major 003
  mknod ${path}/${name}-4 c $major 004
  mknod ${path}/${name}-5 c $major 005
  mknod ${path}/${name}-6 c $major 006
  mknod ${path}/${name}-7 c $major 007
  mknod ${path}/${name}-8 c $major 008
  mknod ${path}/${name}-9 c $major 009
  fi
  chmod 777 $path
  上述代码中rm -f ${path}/${name}*的作用為卸载之前的模块以释放空间在模块卸载中也会用到,之后的代码为进行模块加载。其中mknod命令用于创建设备文件。最后的chmod 777 $path表示赋予读、写以及运行的权限。
  2.模块卸载
  echo -n "Clear existing device nodes..... "
  rm -f $path/${name}*
  echo "Ok (${path}/${name})"
  卸载模块代码较为简单使用rm -f $path/${name}*指令卸载即可。
  4.结论
  Linux系统对于开发人员而言有着众多的优点,但市场上Linux发行版本多,很多硬件设备缺少对应版本的驱动程序,需要自行开发。本文以PC-6325A板卡驱动开发为例介绍了PC-6325A板卡信息及工作原理,linux内核编译加载、常用函数方法实现等基本编程技术。可为Linux系统驱动开发人员提供参考。
  参考文献
  [1]LINUX设备驱动程序第三版,中国电力出版社,2006年1月,魏永明,耿岳,钟书毅 译.
  [2]PC-6325A板卡用户手册.
其他文献
本文基于功能安全的定义和作用,主要介绍了新版IEC61508国际标准的目的和构成框架、风险识别和控制方法,并通过标准应用实例和导读,为相关行业提供帮助,以实现设备、产品、环
模拟计算机已被用作模拟高增益自适应控制的手段,非线性元件(即开—关继电器)在正向通道中维持高增益,这对于在系统的输出与输入之间形成1比1的关系是必需的。然而,高增益会
2003年,十届全国人大四次会议批准的国民经济和社会发展第十一个五年规划纲要明确提出了“全面实施农村中小学远程教育”的构想.转年,“农村中小学现代远程教育工程(以下简称
摘 要: 本文采用文献资料法,问卷调查法,数理统计法等进行研究,对常德市羽毛球运动消费情况进行调查。结果显示:常德市羽毛球消费结构不合理,在羽毛球消费支出的比例相对较高,参与羽毛球消费水平相对较低,常德市羽毛球消费,处于低级阶段。影响常德市羽毛球消费的因子有四大类:个体因子、社会环境因子、媒体宣传因子、全面健身因子。  关键词: 消费;消费结构;消费因子  1 研究对象与方法  1.1 研究对象 
可以肯定的是,气候变化越来越成为人们热议的话题,很多问题也随之出现.然而在这一标题和口号之外,企业发现他们必须为气候变化所带来的潜在影响以及一系列潜在的调控挑战而采
本文记述了用材质为1Cr18Ni9Ti的φ36×2不锈钢管弯制中径为φ270圆形螺管的无芯冷弯试验过程。最终,在常规的基础上设计和使用了带跟进部分的压轮——跟进式压轮,使得所弯成
摘 要: 安全审计是涉密网络安全管理工作的重要环节。本文就如何开展涉密网络的安全审计工作进行了讨论,并提出了涉密网络安全审计的工作流程。  关键词: 信息安全;涉密网络;安全审计  1、引言  随着涉密网络的建成并在科研生产、科研管理、科研服务等方面的广泛应用,作为涉密网络安全管理重要环节之一的安全审计,其重要性已经被普遍认识。但是,面对一个运行中的涉密网络,应该如何按照有关安全管理要求来开展安全
通过对浙江出口企业的问卷调查,结果表明标准化对企业技术创新既有促进作用,也具有阻碍作用.本文探讨了企业如何利用欧盟标准化作为技术创新的信息源进行创新活动,进而提高技
摘 要: 高职院校数控技术专业一体化课程体系建设是高职院校人才培养目标最重要的支撑,具有重要意义,是人才培养模式的重要体现。数控技术专业一体化课程体系不仅是所有课内教学的集合,而且包括了专业教学全部内容的整体安排,例如第二课堂和学生自主实践学习的安排。数控技术专业一体化课程体系建设,不但创新所有课程教学的模式和方法,也能够创新教学管理方式,以适应新的教学要求。本文从调研分析、研究目标、研究内容、研
摘 要:随着社会的发展,建筑行业的技术水平也有了提高,社会对建筑工程的施工技术要求随着提升,建筑的施工中如果出现质量问题,会使工程受到较大的影响。水利工程的施工技术方法应用不合理,会使工程出现损坏,从而造成严重的损失,所以加强水利工程的碾压混凝土重力坝施工有着重要的意义,增强坝体的抗滑性和稳定性。  关键词:岩基;混凝土重力坝;抗滑稳定性  目前在水利工程中,坝基稳定性降低导致坝体发生坍塌问题,造