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

/*

*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;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值