基于嵌入式Linux的MPC850 USB 驱动程序的实现

2019-07-13 00:57发布

摘  要: 本文介绍了嵌入式Linux下设备驱动程序的编写原理,并阐述了在Linux下MPC850 USB的驱动程序的具体实现。
关键词: 嵌入式Linux;驱动程序;USB;主控制器
Linux驱动程序的基本原理
Linux下开发设备驱动程序的原理较之Windows系统来说结构层次简单明了。Linux操作系统中定义了三种设备,即字符设备、块设备和网络设备。字符设备是指存取时没有缓存的设备。块设备的读写都有缓存来支持,并且块设备必须能够随机存取,字符设备则没有这个要求,USB设备主要通过快速串行通信来读写数据,一般把它作为字符设备处理。
图1  USB驱动程序组成 挂在Linux系统上的每个设备都被描述为设备驱动程序文件,一些与设备有关的设备参数文件被保存在/dev目录下。用户自己提供或编写设备驱动时,也需要在/dev目录下有一个设备文件。设备驱动程序可以分为三个主要组成部分:一是自动配置和初始化子程序  二是服务于I/O请求的子程序  三是中断服务子程序。
用户对设备的访问主要有如下函数:
open,打开设备,典型的用法如下:open("/dev/xxx", flag); flag指定打开的参数,例如读写属性等;open函数返回一个整数fd句柄,如果fd小于0,表示打开错误。
read, write读写函数,用法如下:read(int fd, char *buf, char length,..)。
ioctrl设备控制函数,用户各类设备的特殊控制。
驱动程序的设计就是实现上述四个函数与外加一个设备初始化函数。这些函数在设备驱动程序中可以是dev_init(),dev_open(),dev_read(), dev_write(),dev_ioctrl()等。声明一个称之为file operation的结构体将用户级的open等函数与设备dev_open()等函数联系起来。例如
static struct file_operations dev-xxx_fops = {
NULL, /* lseek */
dev_read, /* read */
dev_write, /* write */
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
dev_open,
dev_close,
NULL /* fsync */
};
为了将用户驱动程序与内核结合起来,首先向内核注册,请求内核分配主设备号与次设备号;二是注册中断,以便外部设备向CPU发出中断后能够处理设备中断函数。这些操作的一半在dev_init()函数内完成。dev_init()函数除了注册设备与注册中断外,还要完成设备特有寄存器初始化等操作。驱动程序与内核结合起来后,为了让应用程序能够访问该设备,还需要做一些额外的工作。前面提到,应用程序对设备的访问是通过访问文件的形式实现的,所以要建立一个文件节点与该设备相关联,使用命令 :
mknod  /dev/xxx option  major minor
可以实现上述的关联。该命令的含义是建立一个/dev/xxx的文件节点,并且把特定主设备号与次设备号的设备与该节点关联。Option选项指定设备的类型,字符型还是块设备。有了关联后,应用程序要访问该设备,只要调用open (“dev/xxx”,...)函数即可。 MPC850 USB
MPC850是Motorola公司生产的32位嵌入式通信处理器,具有强大的通信和网络协议处理能力,特别适用于嵌入式通信和网络系统,它提供的功能与MPC860相似,在MPC860系统功能基础上增加了对USB的支持。
MPC850 USB不提供任何OHCI和UHCI接口,但它有作为主机控制器必须的功能。MPC850 USB没有与根集线器连接,当不止一个USB设备时需接一个外部集线器。在MPC850中主控制器类似于功能单元(function),它不遵循OHCI和UHCI标准。USB控制器由一个发送模块、一个接收模块、两个协议状态机构组成。一个协议机构执行功能状态表,另一个执行主机状态表。USB控制器可完成一个USB功能端点,一个USB主机和两种检测的任务。主控制器的特点如下: USB驱动程序由主控制驱动程序、USB子系统、USB设备驱动程序,如图1所示。
在Linux系统中存在一个连接USB设备驱动程序和主控制器驱动程序的子系统USBcore,它通过定义一些数据结构、宏和功能函数来抽象所有的硬件设备。USBcore提供了为硬件处理的所有下层接口,包含所有USB设备驱动和主机控制的通用程序,可称为UpperAPI和LowerAPI。USB子系统提供与设备驱动程序的接口,读取并解析USB设备描述符,配置描述符,为USB设备分配唯一的地址,使用默认的配置来配置设备,支持基本的USB命令请求,连接设备与相应的驱动程序,转发设备驱动程序的数据包。
虽然前面讲了驱动程序有两种加载模式,但是,如果 USBcore或主控制器驱动程序是内核模式,USB设备驱动程序就以模块方式加载。用usb_register和usb_deregister向USBcore子系统注册和注销驱动程序。所有USB驱动程序都有一个向子系统注册的结构。
struct usb_driver{
const char *name;                  /*模块名字*/
void *(*probe, struct usb_device*, unsigned int);    /*probe 功能入口点*/
void (*disconnect, struct usb_device*, void*);      /*disconnect功能入口点*/
struct list_head driver_list;    /*子系统内部初始化*/
struct file_operations fops;    /对驱动函数文件列表*/
int minor;
}
在高速USB中时序安排是以1帧为基本要素的,每1ms为1帧,在每帧要产生并发送SOF包之外,还需要一个微码通道。同步传输和中断传输占90%的有效带宽,控制传输占10%,剩下的为块传输,如下表所示:
SOF Isochronous Interrupt Control Bulk
控制传输完成了设备的配置,设备的轮流检测是由中断传输完成,大量的数据传输由块传输和同步传输完成。
MPC850是不支持这样的时序调度的,我们用1ms的时序器产生一个中断并在软件中运行这个调度。每个SOF时序里我们都加入一个SOF包到tx列表中,并监视发送包的列表,包是依据优先权来加入的。在每帧开始时,检查未处理事务,将符合优先级的队列元素放到当前帧中。驱动程序跟踪处理事务的设备,并不再向它发送事务。程序中用一个队列结构来完成这一调度的。如: 下面是分派队列元素到发送帧的结构。
struct m8xxhci_qe *
take_from_current_frame(int qtype)
{
  struct m8xxhci_private *hp = (struct m8xxhci_private *)m8xxhci_ptr;
  unsigned long flags;
  struct list_head *list;
  struct m8xxhci_qe *qe = NULL;
  ┋
  if (!list_empty(list)) {
  ┋ 
  qe->on_frame_list = 0;
  ┋
  }
  spin_unlock_irqrestore(&framelist_lock, flags);
  return qe;
  }
MPC850 USB主控制器的有些任务是硬件不支持的,必须由软件来实现。例如,发生错误及错误恢复后的重传、每秒产生和传输SOF、CRC5的产生等。这些功能是通过相应的结构体来完成,主控制器驱动向USBcore数据结构注册它的功能函数,它完成接口硬件的初始化、状态控制、传输差错控制、发送SOF帧、数据处理。
结构的每一个成员的名字都对应着一个系统调用,剩下的主要是子函数及结构体的编写,而编写子函数就很容易了。 结语
嵌入式Linux以及USB设备的应用越来越广泛。本文分析了MPC850 USB控制器的特点,描述了在嵌入式Linux操作系统下设备驱动程序的实现,对于使用PowerPC 系列CPU的开发人员具有一定的借鉴作用。■ 参考文献:
1.  ALESSANDRO RUBINI,‘LINUX设备驱动程序’,中国电力出版社,2000
2. 王学龙,‘嵌入式Linux系统设计与应用’,清华大学出版社,2001