Preface

 

USB体系支持多种类型的设备。

在 Linux内核,所有的USB设备都使用 usb_driver结构描述。

   对于不同类型的 USB设备,内核使用传统的设备驱动模型建立设备驱动描述,然后映射到 USB设备驱动,最终完成特定类型的 USB设备驱动

   USB驱动·入门http://infohacker.blog.51cto.com/6751239/1226257

 


USB串口驱动

 

   USB串口驱动关键是向内核注册串口设备结构,并且设置串口的操作。

下面是一个典型的USB设备驱动分析。


1、驱动初始化函数

 

   usb_serial_init()函数是一个典型的 USB设备驱动初始化函数,定义如下:

static int __init usb_serial_init(void)
{
int i;
int result;
usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS); //申请 tty设备驱动描述
if (!usb_serial_tty_driver)
return -ENOMEM;
/* Initialize our global data */
for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
serial_table[i] = NULL;
}
result = bus_register(&usb_serial_bus_type); //注册总线
if (result) {
err("%s - registering bus driver failed", __FUNCTION__);
goto exit_bus;
}
usb_serial_tty_driver->owner = THIS_MODULE; //初始化串口驱动描述
usb_serial_tty_driver->driver_name = "usbserial"; //串口驱动名称
usb_serial_tty_driver->name = "ttyUSB"; //设备文件系统存放路径
usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; //串口设备主设备号
usb_serial_tty_driver->minor_start = 0; //串口设备从设备号起始 ID
usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; //设备类型
usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; //设备子类型
usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; //设备初始化标志
usb_serial_tty_driver->init_termios = tty_std_termios; //串口设备描述
usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; //串口设备初始化参数
tty_set_operations(usb_serial_tty_driver, &serial_ops); //串口设备操作函数
result = tty_register_driver(usb_serial_tty_driver); //注册串口驱动
if (result) {
err("%s - tty_register_driver failed", __FUNCTION__);
goto exit_reg_driver;
}
/* register the USB driver */
result = usb_register(&usb_serial_driver); //注册 USB驱动
if (result < 0) {
err("%s - usb_register failed", __FUNCTION__);
goto exit_tty;
}
/* register the generic driver, if we should */
result = usb_serial_generic_register(debug);
if (result < 0) {
err("%s - registering generic driver failed", __FUNCTION__);
goto exit_generic;
}
info(DRIVER_DESC);
return result;
exit_generic:
usb_deregister(&usb_serial_driver); //注销串口设备
exit_tty:
tty_unregister_driver(usb_serial_tty_driver); //注销 USB串口设备
exit_reg_driver:
bus_unregister(&usb_serial_bus_type); //注销总线
exit_bus:
err ("%s - returning with error %d", __FUNCTION__, result);
put_tty_driver(usb_serial_tty_driver);
return result;
}

函数首先调用 alloc_tty_driver()函数分配一个串口驱动描述符;然后设置串口驱动的属性,包括驱动的主从设备号、设备类型、串口初始化参数等;串口驱动描述符设置完毕后,调用 usb_register()函数注册 USB串口设备。


2、驱动释放函数

 

   驱动释放函数用来释放 USB串口设备驱动申请的内核资源,函数定义如下:

static int __init usb_serial_init(void)
{
int i;
int result;
usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS); //申请 tty设备驱动描述
if (!usb_serial_tty_driver)
return -ENOMEM;
/* Initialize our global data */
for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
serial_table[i] = NULL;
}
result = bus_register(&usb_serial_bus_type); //注册总线
if (result) {
err("%s - registering bus driver failed", __FUNCTION__);
goto exit_bus;
}
usb_serial_tty_driver->owner = THIS_MODULE; //初始化串口驱动描述
usb_serial_tty_driver->driver_name = "usbserial"; //串口驱动名称
usb_serial_tty_driver->name = "ttyUSB"; //设备文件系统存放路径
usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; //串口设备主设备号
usb_serial_tty_driver->minor_start = 0; //串口设备从设备号起始 ID
usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; //设备类型
usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; //设备子类型
usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; //设备初始化标志
usb_serial_tty_driver->init_termios = tty_std_termios; //串口设备描述
usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; //串口设备初始化参数
tty_set_operations(usb_serial_tty_driver, &serial_ops); //串口设备操作函数
result = tty_register_driver(usb_serial_tty_driver); //注册串口驱动
if (result) {
err("%s - tty_register_driver failed", __FUNCTION__);
goto exit_reg_driver;
}
/* register the USB driver */
result = usb_register(&usb_serial_driver); //注册 USB驱动
if (result < 0) {
err("%s - usb_register failed", __FUNCTION__);
goto exit_tty;
}
/* register the generic driver, if we should */
result = usb_serial_generic_register(debug);
if (result < 0) {
err("%s - registering generic driver failed", __FUNCTION__);
goto exit_generic;
}
info(DRIVER_DESC);
return result;
exit_generic:
usb_deregister(&usb_serial_driver); //注销串口设备
exit_tty:
tty_unregister_driver(usb_serial_tty_driver); //注销 USB串口设备
exit_reg_driver:
bus_unregister(&usb_serial_bus_type); //注销总线
exit_bus:
err ("%s - returning with error %d", __FUNCTION__, result);
put_tty_driver(usb_serial_tty_driver);
return result;
}

3、串口操作函数

 

USB串口设备驱动使用了一个 tty_operations类型的结构,该结构包含了串口的所有操作。定义如下:

static struct tty_operations serial_ops = {
.open = serial_open, //打开串口
.close = serial_close, //关闭串口
.write = serial_write, //串口写操作
.write_room = serial_write_room,
.ioctl = serial_ioctl, // I/O控制操作
.set_termios = serial_set_termios, //设置串口参数
.throttle = serial_throttle,
.unthrottle = serial_unthrottle,
.break_ctl = serial_break, // break信号处理
.chars_in_buffer = serial_chars_in_buffer, //缓冲处理
.read_proc = serial_read_proc, //串口读操作
.tiocmget = serial_tiocmget, //获取 I/O控制参数
.tiocmset = serial_tiocmset, //设置 I/O控制参数
};

   serial_ops结构变量设置的所有串口操作函数,均使用内核 USB核心提供的标准函数,定义在/drivers/usb/serial/generic.c文件中


USB键盘驱动

 

USB键盘驱动与串口驱动结构类似,不同是的使用USB设备核心提供的usb_keyboard_driver结构作为设备核心结构。下面是 USB键盘驱动的重点部分。


1、驱动初始和注销

 

   USB键盘驱动初始化和注销函数定义如下:

static int __init usb_kbd_init(void)
{
int result = usb_register(&usb_kbd_driver); //注册 USB设备驱动
if (result == 0)
info(DRIVER_VERSION ":" DRIVER_DESC);
return result;
}
static void __exit usb_kbd_exit(void)
{
usb_deregister(&usb_kbd_driver); //注销 USB设备驱动
}

 

   usb_kbd_init()函数在驱动加载的时候调用,该函数使用 usb_register()函数向内核注册一个 USB设备驱动;usb_kbd_exit()函数在卸载驱动程序的时候调用,该函数使用 usb_deregister()函数注销 USB设备。初始化和注销函数使用了 usb_keyboard结构变量,用于描述 USB键盘驱动程序,定义如下:

//usb_driver结构体
static struct usb_driver usb_keyboard =
{
.name = "usbkbd", //驱动名称
.probe = usb_kbd_probe, //检测设备函数
.disconnect = usb_kbd_disconnect, //断开连接函数
.id_table = usb_kbd_id_table, //设备 ID
};

从 usb_keyboard结构定义看出,usb_kbd_probe()函数是设备检测函数;

   usb_kbd_disconnect()函数是断开设备连接。


2、设备检测函数

 

   设备检测函数在插入 USB设备的时候被USB文件系统调用,负责检测设备类型是否与驱动相符。如果设备类型与驱动匹配,则向 USB核心注册设备。

函数定义如下:

static int usb_kbd_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(iface);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_kbd *kbd;
struct input_dev *input_dev;
int i, pipe, maxp;
interface = iface->cur_altsetting;
if (interface->desc.bNumEndpoints != 1) //检查设备是否符合
return -ENODEV;
endpoint = &interface->endpoint[0].desc;
if (!(endpoint->bEndpointAddress & USB_DIR_IN))
return -ENODEV;
if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)
return -ENODEV;
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //创建端点的管道
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);
input_dev = input_allocate_device(); //分配 input_dev结构体
if (!kbd || !input_dev) //分配设备结构占用的内存
goto fail1;
if (usb_kbd_alloc_mem(dev, kbd))
goto fail2;
kbd->usbdev = dev;
kbd->dev = input_dev;
if (dev->manufacturer) //检查制造商名称
strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
if (dev->product) { //检查产品名称
if (dev->manufacturer)
strlcat(kbd->name, " ", sizeof(kbd->name));
strlcat(kbd->name, dev->product, sizeof(kbd->name));
}
if (!strlen(kbd->name))
snprintf(kbd->name, sizeof(kbd->name),
"USB HIDBP Keyboard %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));
//初始化输入设备
input_dev->name = kbd->name; //输入设备名称
input_dev->phys = kbd->phys; //输入设备物理地址
usb_to_input_id(dev, &input_dev->id); //输入设备 ID
input_dev->cdev.dev = &iface->dev;
input_dev->private = kbd;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);
for (i = 0; i < 255; i++)
set_bit(usb_kbd_keycode[i], input_dev->keybit);
clear_bit(0, input_dev->keybit);
input_dev->event = usb_kbd_event;
input_dev->open = usb_kbd_open;
input_dev->close = usb_kbd_close;
//初始化中断 urb
usb_fill_int_urb(kbd->irq, dev, pipe,
kbd->new, (maxp > 8 ? 8 : maxp),
usb_kbd_irq, kbd, endpoint->bInterval);
kbd->irq->transfer_dma = kbd->new_dma;
kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
kbd->cr->bRequest = 0x09;
kbd->cr->wValue = cpu_to_le16(0x200);
kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
kbd->cr->wLength = cpu_to_le16(1);
//初始化中断 urb
usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
(void *) kbd->cr, kbd->leds, 1,
usb_kbd_led, kbd);
kbd->led->setup_dma = kbd->cr_dma;
kbd->led->transfer_dma = kbd->leds_dma;
kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
input_register_device(kbd->dev); //注册输入设备
usb_set_intfdata(iface, kbd); //设置接口私有数据
return 0;
fail2: usb_kbd_free_mem(dev, kbd);
fail1: input_free_device(input_dev);
kfree(kbd);
return -ENOMEM;
}

 

   函数一开始检测设备类型,如果与驱动程序匹配,则创建 USB设备端点,分配设备驱动结构占用的内存。分配好设备驱动使用的结构后,申请一个键盘设备驱动节点,然后设置键盘驱动最后设置 USB设备的中断 URB和控制 URB,供 USB设备核心使用。


3、设备断开连接函数

 

   在设备断开连接的时候,USB文件系统会调用 usb_kbd_disconnect()函数,释放设备占用的资源。

函数定义如下:

static void usb_kbd_disconnect(struct usb_interface *intf)
{
struct usb_kbd *kbd = usb_get_intfdata (intf);
usb_set_intfdata(intf, NULL); //设置接口私有数据为 NULL
if (kbd) {
usb_kill_urb(kbd->irq); //终止 URB
input_unregister_device(kbd->dev); //注销输入设备
usb_kbd_free_mem(interface_to_usbdev(intf), kbd); //释放设备驱动占用的内存
kfree(kbd);
}
}

   usb_kbd_disconnect()函数释放 USB键盘设备占用的 URB资源,然后注销设备,最后调用usb_kbd_free_mem()函数,释放设备驱动结构变量占用的内存。

本文出自 “成鹏致远” 博客,请务必保留此出处http://infohacker.blog.51cto.com/6751239/1227045

【驱动】USB驱动实例·串口驱动·键盘驱动的更多相关文章

  1. Linux下的硬件驱动——USB设备(转载)

    usb_bulk_msg函数 当对usb设备进行一次读或者写时,usb_bulk_msg 函数是非常有用的; 然而, 当你需要连续地对设备进行读/写时,建议你建立一个自己的urbs,同时将urbs 提 ...

  2. ARM-Linux S5PV210 UART驱动(5)----串口的open操作(tty_open、uart_open)

    串口驱动初始化后,串口作为字符驱动也已经注册到系统了,/dev目录下也有设备文件节点了. 那接下来uart的操作是如何进行的呢? 操作硬件之前都是要先open设备,先来分析下这里的open函数具体做了 ...

  3. 【驱动】USB驱动实例·串口驱动·键盘驱动【转】

    转自:http://www.cnblogs.com/lcw/p/3159370.html Preface USB体系支持多种类型的设备. 在 Linux内核,所有的USB设备都使用 usb_drive ...

  4. linux驱动基础系列--Linux 串口、usb转串口驱动分析

    前言 主要是想对Linux 串口.usb转串口驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如字符设备驱动.平台驱动等也不进行详细说明原理.如果有任何错误地方,请指出, ...

  5. 8.2 USB键盘驱动编写和测试

    目标:根据USB驱动分析和上节的USB鼠标驱动,编写键盘驱动,并测试. 一.原理分析 1. 首先通过打印usb_buf[i]中的8字节数据,看一下按键按下之后会接收到什么. 1)通过按完所有键盘按键打 ...

  6. USB键盘驱动分析

    简介 本文介绍USB驱动程序编写的流程,分析一个键盘的USB程序,基于linux-2.6.39 USB驱动概要 分层 主机层面的USB驱动的整体架构可以分成4层,自顶到下依次是 1.USB设备驱动:本 ...

  7. 《连载 | 物联网框架ServerSuperIO教程》- 14.配制工具介绍,以及设备驱动、视图驱动、服务实例的挂载

    注:ServerSuperIO二次开发套件授权码申请---截止到:2016-12-09 1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架Server ...

  8. 《连载 | 物联网框架ServerSuperIO教程》-4.如开发一套设备驱动,同时支持串口和网络通讯。附:将来支持Windows 10 IOT

    1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...

  9. ARM-Linux S5PV210 UART驱动(3)----串口核心层、关键结构体、接口关系

    尽管一个特定的UART设备驱动完全可以按照tty驱动的设计方法来设计,即定义tty_driver并实现tty_operations其中的成员函数,但是Linux已经在文件serial_core.c中实 ...

随机推荐

  1. HBase Go客户端Row构造注意事项

    1. Hbase 的Go客户端语言使用方法 2. Hbase的Row使用注意事项 2.1. Row的前几个字段尽量散列 2.2. Row的排序是把所有Row中的字符做字典排序 我们最近在一个项目中使用 ...

  2. 使用extract-text-webpack-plugin提取css文件

    一.css之上的语言 js之上的语言有jsx.tyepscript.coffescript. html之上的语言有jade. css之上的语言有sass.scss.less.stylus,这几种语言区 ...

  3. 使用Idea添加PYTHONPATH的一种方案

    工作中我们常常需要更改PYTHONPATH,为项目添加一些依赖. 而不同的项目依赖的PYTHONPATH是不一样的,这就导致项目之间的PYTHONPATH发生混乱. 另一方面,有的电脑上PYTHON2 ...

  4. Cobbler自动部署主机系统

    Cobbler自动部署主机系统 简介: Cobbler由python语言开发,是对PXE和 Kickstart以及DHCP的封装.融合很多特性,提供了CLI和Web的管理形式.更加方便的实行网络安装. ...

  5. Redis学习之路(003)- hiredis安装及测试

    一. hiredis下载地址及C API  github下载:https://github.com/redis/hiredis 安装脚本: #!/bin/zsh git clone https://g ...

  6. Linux下面安装和配置MySQL

    如何从MySQL官方Yum仓库安装MySQL5.6 首先我们需要从MySQL开发者网站下载Yum仓库文件 Download MySQL Yum Repository 从上面的连接地址下载:Red Ha ...

  7. Maven最佳实践:版本管理【转】

    什么是版本管理 首先,这里说的版本管理(version management)不是指版本控制(version control),但是本文假设你拥有基本的版本控制的知识,了解subversion的基本用 ...

  8. iOS中app的分发测试

    知识的学习在于分享.分享出来才能共同进步. 关于测试 有几种方式 1.开发人员直接在电脑上 用模拟器 2. 真机调试,测试人员可以拿着测试机找开发人员在电脑上跑真机测试 3. 公司和个人账号  直接 ...

  9. QQMacMgr for Mac(腾讯电脑管家)安装

    1.软件简介    腾讯电脑管家是 macOS 系统上一款由腾讯公司带来到的安全管理软件.功能有垃圾清理.软件仓库.小火箭加速和防钓鱼等.而在视觉 UI 上,导入星空概念,操作过场动画全部以星空为题材 ...

  10. js事件之event.preventDefault()与(www.111cn.net)event.stopPropagation()用法区别

    event.preventDefault()用法介绍 该方法将通知 Web 浏览器不要执行与事件关联的默认动作(如果存在这样的动作).例如,如果 type 属性是 "submit" ...