/*

*By Neil Chiao ()

*欢迎到“新星湾()”指导

*/

在X86平台的主板上一般都有一个蜂鸣器,有人可能认为这么简单的东西,根本不需要驱动吧?但是其实Linux内核中专门有一个这样的驱动pcspkr.c。

(注意:本文分析的代码来自linux-2.6.28)

1、从用户空间代码开始

先看下面这个小程序,此程序的作用是让PC蜂鸣器叫,它open了/dev/tty10这个设备,难道PC蜂鸣器设备节点是/dev/tty10??

#include

#include

#include

int main(int argc, char *argv[])

{

int fd = open("/dev/tty10", O_RDONLY);

if (fd == -1 || argc != 3) return -1;

return ioctl(fd, KDMKTONE, (atoi(argv[2])<<16)+(1193180/atoi(argv[1])));

}

2、定位到内核中tty的ioctl实现(KDMKTONE)

其实,上述代码中,把/dev/tty10修改成tty2,tty5随便一个都是可以的(我试过的)。

下面定位到内核的tty实现,发现在vt_ioctl.c中有如下代码:

vt_ioctl()

{

......

case KDMKTONE:

if (!perm)

goto eperm;

{

unsigned int ticks, count;

ticks = HZ * ((arg >> 16) & 0xffff) / 1000;

count = ticks ? (arg & 0xffff) : 0;

if (count)

count = CLOCK_TICK_RATE / count;

kd_mksound(count, ticks);

break;

}

......

}

上述代码最终调用kd_mksound函数来发声。kd_mksound函数实现如下:

void kd_mksound(unsigned int hz, unsigned int ticks)

{

struct list_head *node;

del_timer(&kd_mksound_timer);

if (hz) {

list_for_each_prev(node, &kbd_handler.h_list) {

struct input_handle *handle = to_handle_h(node);

if (test_bit(EV_SND, handle->dev->evbit)) {

if (test_bit(SND_TONE, handle->dev->sndbit)) {

input_inject_event(handle, EV_SND, SND_TONE, hz);

break;

}

if (test_bit(SND_BELL, handle->dev->sndbit)) {

input_inject_event(handle, EV_SND, SND_BELL, 1);

break;

}

}

}

if (ticks)

mod_timer(&kd_mksound_timer, jiffies + ticks);

} else

kd_nosound(0);

}

由代码,我们知道,kd_mksound函数实质上使用input_inject_event(handle, EV_SND, SND_TONE, hz)来触发了一个input事件,来使pc speaker叫。

3、input event机制

这里就涉及到了input子系统了,这个子系统还是比较复杂的,呵。

上述代码中的input_inject_event按下面顺序调用:

input_inject_eventà

input_handle_event(dev, type, code, value);à

input_pass_event

其中,input_handle_event如下:

static void input_handle_event(struct input_dev *dev,

unsigned int type, unsigned int code, int value)

{

int disposition = INPUT_IGNORE_EVENT;

switch (type) {

......

//PC

case EV_SND:

if (is_event_supported(code, dev->sndbit, SND_MAX)) {

if (!!test_bit(code, dev->snd) != !!value)

__change_bit(code, dev->snd);

disposition = INPUT_PASS_TO_ALL;

}

break;

......

}

......

if (disposition & INPUT_PASS_TO_HANDLERS)

input_pass_event(dev, type, code, value);

}

input_pass_event函数实现如下:

static void input_pass_event(struct input_dev *dev,

unsigned int type, unsigned int code, int value)

{

struct input_handle *handle;

rcu_read_lock();

handle = rcu_dereference(dev->grab);

if (handle)

handle->handler->event(handle, type, code, value);

else

list_for_each_entry_rcu(handle, &dev->h_list, d_node)

if (handle->open)

handle->handler->event(handle,

type, code, value);

rcu_read_unlock();

}

PC蜂鸣器的event实现

input子系统根据input event的类型(PC蜂鸣器是EV_SND),最终调用相应的event处理,PC蜂鸣器的event在pcspkr.c中实现:

static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)

{

unsigned int count = 0;

unsigned long flags;

if (type != EV_SND)

return -1;

switch (code) {

case SND_BELL: if (value) value = 1000;

case SND_TONE: break;

default: return -1;

}

if (value > 20 && value < 32767)

count = PIT_TICK_RATE / value;

spin_lock_irqsave(&i8253_lock, flags);

printk("count = %d\n",count);

//下面一段是让PC蜂鸣器叫最实质的实现,想看懂的话,请自己找ICH8南桥芯片手册看

if (count) {

outb_p(inb_p(0x61) | 3, 0x61);

outb_p(0xB6, 0x43);

outb_p(count & 0xff, 0x42);

outb((count >> 8) & 0xff, 0x42);

} else {

outb(inb_p(0x61) & 0xFC, 0x61);

}

spin_unlock_irqrestore(&i8253_lock, flags);

return 0;

}

linux蜂鸣器驱动指令,Linux 设备驱动简析—PC蜂鸣器驱动相关推荐

  1. linux c蜂鸣器驱动程序,Linux 设备驱动简析—PC蜂鸣器驱动

    /* *By Neil Chiao () *欢迎到"新星湾()"指导 */ 在X86平台的主板上一般都有一个蜂鸣器,有人可能认为这么简单的东西,根本不需要驱动吧?但是其实Linux ...

  2. linux蜂鸣器驱动指令,linux蜂鸣器驱动 蜂鸣器--LINUX.doc

    linux蜂鸣器驱动 蜂鸣器--LINUX 导读:就爱阅读网友为您分享以下"蜂鸣器--LINUX"的资讯,希望对您有所帮助,感谢您对92的支持! //mux = 1/16 tcfg ...

  3. Linux设备驱动简析—PC重启源码分析

    Linux在PC上的关机和重启可能由两种行为引发,一是通过用户编程,一是系统自己产生的消息.用户和系统进行交互的方式也有两个,一个是系统调用:sys_reboot,另一个就是apm或acpi的设备文件 ...

  4. Linux设备树简析

    1. 前言 限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺. 2. 设备树的来源 在 Linux 中,每个设备驱动,管理一组设备数据,类似面向对象编程中类和其实例对象的关 ...

  5. Linux进程描述符task_struct结构体简析

    进程是处于执行期的程序以及它所管理的资源(如打开的文件.挂起的信号.进程状态.地址空间等等)的总称 Linux内核通过一个被称为进程描述符的task_struct结构体来管理进程,这个结构体包含了一个 ...

  6. linux中swi指令,Linux系统调用、新增系统调用方法

    说明: 系统调用是内核和应用程序间的接口,应用程序要访问硬件设备和其他操作系统资源,可以通过系统调用来完成. 在linux中,系统调用是用户空间访问内核的一种手段,除异常和中断外,他们是进入内核的合法 ...

  7. linux ping大包指令,linux下ping命令使用详解,

    linux下ping命令使用详解, •ping命令一般用于检测网络通与不通,也叫时延,其值越大,速度越慢PING(PacketInternetGrope),因特网包探索器,用于测试网络连接量的程序. ...

  8. linux修改时间指令,Linux 修改时间的指令

    查询现在的时间 date "+%Y%m%d%H%M.%S" %Y->年 %m->月 %d->日 %H->时 %M->分 %S->秒 查出来后可直 ...

  9. linux df -h指令,Linux df 命令使用参数详解

    df命令用来检查linux系统的文件系统的磁盘空间使用情况. 语法及格式 df [选项] [文件名] 常见参数 -a:--all,显示所有的文件系统,包括虚拟文件系统,参考示例2. -B:--bloc ...

  10. linux拨号上网指令,linux中的pppoe拨号上网

    实例:(linux中的pppoe拨号上网) ①安装软件:把下载的pppoe传到linux下的/root目录下,在终端对软件进行拆包: ll:查看目录,能看见软件包的名字 tar -zxvf rp-pp ...

最新文章

  1. 使用 OpenMVG+PMVS实现视觉三维重建
  2. vs2012下 error4996
  3. 如何获取 docker 容器(container)的 ip 地址
  4. ASP.NET MVC+EF框架+EasyUI实现权限管理系列(18)-过滤器的使用和批量删除数据(伪删除和直接删除)...
  5. puppet(1.1-1.6)
  6. 【Elasticsearch】Elasticsearch 动态模板(Dynamic templates)
  7. FFmpeg总结(七)AV系列结构体之AVIOContext
  8. pearson相关系数_pearson相关系数与典型相关性分析(CCA)
  9. 【2020春招记录】 吉比特游戏研发笔试
  10. LG化学成为海水淡化领域膜领导品牌后,大举进军苦咸水反渗透膜市场
  11. 用python提取字符串的中英文——建议收藏反复观看
  12. openeuler 欧拉操作系统的几个图形界面安装方法
  13. linux/安卓的spi读写ADS1256出现读写错误
  14. win32asm导入表
  15. x722网卡支持百兆吗_用200M宽带,电脑网卡却只有百兆?这样可以解决!
  16. ORA-00257:archiver error.Connect internal only, until freed 问题解决
  17. Covid-19 肺部 X 射线分类和 CT 检测演示
  18. Mysql传智jing_dong数据库
  19. stdafx.h与Afx.h了解
  20. smcsuperio黑苹果_基于OpenCore0.6.1的黑苹果安装,小白也能看

热门文章

  1. 第二周 半导体器件基础(二)
  2. [SAP ABAP开发技术总结]增强Enhancement
  3. 简单线性回归R和Python预测身高体重国内生产总值二氧化碳排放量
  4. FreeRTOS下开启fatfs文件重入功能后,“Error:..\..\FreeRTOS\src\queue.c,1248“报错问题解决
  5. 团购幸存者:团购是个苦生意
  6. 怎么把pdf文件转换成word方法分享
  7. 二维数组更改vue,VueX中直接修改数据报错,修改一维数组,二维数组,报错的原因...
  8. 微信网页扫码登录的实现
  9. java 水波纹_java实现水波纹扩散效果
  10. 计算机中c盘是什么分区,电脑C盘怎么分区