ds1620混杂设备驱动

来源:互联网 发布:nba哪只球队好看 知乎 编辑:程序博客网 时间:2024/05/19 14:19

下面分析一下数字温度计DS1620的驱动程序。先从网上找到其数据手册,看看它的功能,再找到时序图片一下。这样有利于分析驱动,再套用上linux的驱动框架就可以了。




同样,先找驱动的入口和出口,看看他们都做了什么。

module_init(ds1620_init);

module_exit(ds1620_exit);

先来看一些常量的定义吧

/*

* arch/arm/include/asm/therm.h: Definitions for Dallas Semiconductor

* DS1620 thermometer driver (as used in the Rebel.com NetWinder)

*/

#ifndef __ASM_THERM_H

#define __ASM_THERM_H


/* ioctl numbers for /dev/therm */

#define CMD_SET_THERMOSTATE 0x53

#define CMD_GET_THERMOSTATE 0x54

#define CMD_GET_STATUS 0x56

#define CMD_GET_TEMPERATURE 0x57

#define CMD_SET_THERMOSTATE2 0x58

#define CMD_GET_THERMOSTATE2 0x59

#define CMD_GET_TEMPERATURE2 0x5a

#define CMD_GET_FAN 0x5b

#define CMD_SET_FAN 0x5c


#define FAN_OFF 0

#define FAN_ON 1

#define FAN_ALWAYS_ON 2


struct therm {

int hi;

int lo;

};


#endif

定义了一些常量寄存器的值:

/*Definitions for DS1620 chip */

#defineTHERM_START_CONVERT 0xee

#defineTHERM_RESET 0xaf

#defineTHERM_READ_CONFIG 0xac

#defineTHERM_READ_TEMP 0xaa

#defineTHERM_READ_TL 0xa2

#defineTHERM_READ_TH 0xa1

#defineTHERM_WRITE_CONFIG 0x0c

#defineTHERM_WRITE_TL 0x02

#defineTHERM_WRITE_TH 0x01


#defineCFG_CPU 2

#defineCFG_1SHOT 1



staticint__init ds1620_init(void)

{

intret;

structtherm th, th_start;


这个什么东东????

if(!machine_is_netwinder())

return-ENODEV;


这个是设置一些参数?复位,配置,停止转换。

ds1620_out(THERM_RESET,0, 0);

ds1620_out(THERM_WRITE_CONFIG,8, CFG_CPU);

ds1620_out(THERM_START_CONVERT,0, 0);


/*

* Trigger the fan to start by setting

* temperature high point low. This kicks

* the fan into action.

*/

ds1620_read_state(&th);

th_start.lo= 0;

th_start.hi= 1;

ds1620_write_state(&th_start);


msleep(2000);


ds1620_write_state(&th);


这里是注册一个混杂设备(混杂设备就是不知道如何归类的)

ret= misc_register(&ds1620_miscdev);

if(ret < 0)

returnret;


这里创建/proc文件系统节点,方便从/proc中获取信息,这样就不用写标准的测试程序了。

就不用走那套老的程序open(),read(),write(),close().

#ifdefTHERM_USE_PROC

proc_therm_ds1620= create_proc_entry("therm",0, NULL);

if(proc_therm_ds1620)

proc_therm_ds1620->read_proc= proc_therm_ds1620_read;

else

printk(KERN_ERR"therm:unable to register /proc/therm\n");

#endif


读取一下当前状态,输出一下信息。

ds1620_read_state(&th);

ret= cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));


printk(KERN_INFO"Thermostat:high %i.%i, low %i.%i, "

"current%i.%i C, fan %s.\n",

th.hi >> 1, th.hi & 1 ? 5 : 0,

th.lo >> 1, th.lo & 1 ? 5 : 0,

ret >> 1, ret & 1 ? 5 : 0,

fan_state[netwinder_get_fan()]);


return0;

}


staticvoid__exit ds1620_exit(void)

{

移除/proc节点

#ifdefTHERM_USE_PROC

remove_proc_entry("therm",NULL);

#endif

注销混杂设备。

misc_deregister(&ds1620_miscdev);

}


再来看一下混杂设备的结构体及接口函数:

structmiscdevice {

intminor;

constchar*name;

conststructfile_operations*fops;

structlist_headlist;

structdevice*parent;

structdevice*this_device;

constchar*nodename;

mode_tmode;

};


externintmisc_register(structmiscdevice* misc);

externintmisc_deregister(structmiscdevice*misc);

这里是真实代码:

staticstructmiscdeviceds1620_miscdev= {

TEMP_MINOR,

"temp",

&ds1620_fops

};

从文件操作指针可以看来,也只支持open,read,ioctl三个函数。

staticconststructfile_operationsds1620_fops = {

.owner =THIS_MODULE,

.open =ds1620_open,

.read =ds1620_read,

.unlocked_ioctl =ds1620_unlocked_ioctl,

.llseek =no_llseek,

};

看一下打开函数,这里调用了一个奇怪的函数,以前没有见到过呀:

立即F3查看一下

好像就是设置一个这个fd,不支持seek的功能。

staticintds1620_open(structinode*inode,structfile*file)

{

returnnonseekable_open(inode, file);

}

/*

*This is used by subsystems that don't wantseekable

*file descriptors. The function is not supposed to ever fail, the only

*reason it returns an 'int'and not 'void' is so that it can be plugged

*directly into file_operations structure.

*/

intnonseekable_open(structinode*inode,structfile*filp)

{

filp->f_mode&= ~(FMODE_LSEEK|FMODE_PREAD|FMODE_PWRITE);

return0;

}


EXPORT_SYMBOL(nonseekable_open);


这个不管,直接返回0也行。再来看看read()函数。最为关键是读取数据那部分。

到底是怎么读取回来的。这些只是套用linux的驱动框架而已。

staticssize_t

ds1620_read(structfile*file,char__user *buf,size_tcount,loff_t*ptr)

{

signedintcur_temp;

signedcharcur_temp_degF;

读取值并做转换。

cur_temp= cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9)) >> 1;


/*convert toFahrenheit,as per wdt.c */

cur_temp_degF= (cur_temp * 9) / 5 + 32;


拷贝到用户空间。

if(copy_to_user(buf, &cur_temp_degF, 1))

return-EFAULT;


return1;

}

再来看看ioctl()支持的功能,这里使用了互斥锁,有效的防止多线程访问时导致的不可见错误。

staticlong

ds1620_unlocked_ioctl(structfile*file,unsignedintcmd,unsignedlongarg)

{

intret;


mutex_lock(&ds1620_mutex);

ret= ds1620_ioctl(file, cmd, arg);

mutex_unlock(&ds1620_mutex);


returnret;

}

这里的ioctl()支持好几个命令呢。

staticint

ds1620_ioctl(structfile*file,unsignedintcmd,unsignedlongarg)

{

structthermtherm;

union{

structtherm__user *therm;

int__user *i;

}uarg;

inti;


uarg.i= (int__user *)arg;


switch(cmd){

caseCMD_SET_THERMOSTATE:

caseCMD_SET_THERMOSTATE2:

这里检测是否具有能力

if(!capable(CAP_SYS_ADMIN))

return-EPERM;


if(cmd == CMD_SET_THERMOSTATE) {

if(get_user(therm.hi,uarg.i))

return-EFAULT;

therm.lo= therm.hi- 3;

}else{

if(copy_from_user(&therm, uarg.therm,sizeof(therm)))

return-EFAULT;

}


therm.lo<<= 1;

therm.hi<<= 1;


ds1620_write_state(&therm);

break;


caseCMD_GET_THERMOSTATE:

caseCMD_GET_THERMOSTATE2:

ds1620_read_state(&therm);


therm.lo>>= 1;

therm.hi>>= 1;


if(cmd == CMD_GET_THERMOSTATE) {

if(put_user(therm.hi,uarg.i))

return-EFAULT;

}else{

if(copy_to_user(uarg.therm,&therm,sizeof(therm)))

return-EFAULT;

}

break;


caseCMD_GET_TEMPERATURE:

caseCMD_GET_TEMPERATURE2:

i= cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));


if(cmd == CMD_GET_TEMPERATURE)

i>>= 1;


returnput_user(i,uarg.i)? -EFAULT : 0;


caseCMD_GET_STATUS:

i= ds1620_in(THERM_READ_CONFIG, 8) & 0xe3;


returnput_user(i,uarg.i)? -EFAULT : 0;


caseCMD_GET_FAN:

i= netwinder_get_fan();


returnput_user(i,uarg.i)? -EFAULT : 0;


caseCMD_SET_FAN:

if(!capable(CAP_SYS_ADMIN))

return-EPERM;


if(get_user(i,uarg.i))

return-EFAULT;


netwinder_set_fan(i);

break;

default:

return-ENOIOCTLCMD;

}


return0;

}


再来看看子函数的实现:

staticvoidds1620_write_state(structtherm*therm)

{

ds1620_out(THERM_WRITE_CONFIG,8, CFG_CPU);

ds1620_out(THERM_WRITE_TL,9, therm->lo);

ds1620_out(THERM_WRITE_TH,9, therm->hi);

ds1620_out(THERM_START_CONVERT,0, 0);

}


staticvoidds1620_read_state(structtherm*therm)

{

therm->lo= cvt_9_to_int(ds1620_in(THERM_READ_TL, 9));

therm->hi= cvt_9_to_int(ds1620_in(THERM_READ_TH, 9));

}

接着调用子函数:

staticvoidds1620_out(intcmd,intbits,intvalue)

{

unsignedlongflags;


netwinder_lock(&flags);

netwinder_ds1620_set_clk(1);

netwinder_ds1620_set_data_dir(0);

netwinder_ds1620_reset();


udelay(1);


ds1620_send_bits(8,cmd);

if(bits)

ds1620_send_bits(bits,value);


udelay(1);


netwinder_ds1620_reset();

netwinder_unlock(&flags);


msleep(20);

}


staticunsignedintds1620_in(intcmd,intbits)

{

unsignedlongflags;

unsignedintvalue;


netwinder_lock(&flags);

netwinder_ds1620_set_clk(1);

netwinder_ds1620_set_data_dir(0);

netwinder_ds1620_reset();


udelay(1);


ds1620_send_bits(8,cmd);


netwinder_ds1620_set_data_dir(1);

value= ds1620_recv_bits(bits);


netwinder_ds1620_reset();

netwinder_unlock(&flags);


returnvalue;

}


接着嵌套调用,唉,调用这么多

看到send()函数这样写法,很明显是使用GPIO来模拟时序的。低---高。模拟时钟。

staticvoidds1620_send_bits(intnr,intvalue)

{

inti;


for(i = 0; i < nr; i++) {

netwinder_ds1620_set_data(value& 1);

netwinder_ds1620_set_clk(0);

udelay(1);

netwinder_ds1620_set_clk(1);

udelay(1);


value>>= 1;

}

}

这个接收函数也是使用GPIO来模拟时序。

staticunsignedintds1620_recv_bits(intnr)

{

unsignedintvalue = 0, mask = 1;

inti;


netwinder_ds1620_set_data(0);


for(i = 0; i < nr; i++) {

netwinder_ds1620_set_clk(0);

udelay(1);


if(netwinder_ds1620_get_data())

value|= mask;


mask<<= 1;


netwinder_ds1620_set_clk(1);

udelay(1);

}


returnvalue;

}

下面这几个函数就是最终操作的GPIO的高低电平值:

staticinlinevoidnetwinder_ds1620_set_clk(intclk)

{

nw_gpio_modify_op(GPIO_DSCLK,clk ? GPIO_DSCLK: 0);

}


staticinlinevoidnetwinder_ds1620_set_data(intdat)

{

nw_gpio_modify_op(GPIO_DATA,dat ? GPIO_DATA: 0);

}


staticinlineintnetwinder_ds1620_get_data(void)

{

returnnw_gpio_read() & GPIO_DATA;

}


staticinlinevoidnetwinder_ds1620_set_data_dir(intdir)

{

nw_gpio_modify_io(GPIO_DATA,dir ? GPIO_DATA: 0);

}


staticinlinevoidnetwinder_ds1620_reset(void)

{

nw_cpld_modify(CPLD_DS_ENABLE,0);

nw_cpld_modify(CPLD_DS_ENABLE,CPLD_DS_ENABLE);

}


staticinlinevoidnetwinder_lock(unsignedlong*flags)

{

spin_lock_irqsave(&nw_gpio_lock,*flags);

}


staticinlinevoidnetwinder_unlock(unsignedlong*flags)

{

spin_unlock_irqrestore(&nw_gpio_lock,*flags);

}


staticinlinevoidnetwinder_set_fan(inti)

{

unsignedlongflags;


spin_lock_irqsave(&nw_gpio_lock,flags);

nw_gpio_modify_op(GPIO_FAN,i ? GPIO_FAN: 0);

spin_unlock_irqrestore(&nw_gpio_lock,flags);

}


staticinlineintnetwinder_get_fan(void)

{

if((system_rev & 0xf000) == 0x4000)

returnFAN_ALWAYS_ON;


return(nw_gpio_read() & GPIO_FAN)? FAN_ON : FAN_OFF;

}

这里面反复调用了nw_gpio_modify_op这个函数,来看看这个是干什么用的

,这驱动谁写的,搞得嵌套这么多函数,头不疼吗。俺好累呀。

这里的outb()就是输出一个字节的意思啦。最终的输出在这里,

肯定是经过映射后的虚拟地址吧

voidnw_gpio_modify_op(unsignedintmask,unsignedintset)

{

unsignedintnew_gpio, changed;


new_gpio= (current_gpio_op & ~mask) | set;

changed= new_gpio ^ current_gpio_op;

current_gpio_op= new_gpio;


if(changed & 0xff)

outb(new_gpio,GP1_IO_BASE);

if(changed & 0xff00)

outb(new_gpio>> 8, GP2_IO_BASE);

}

EXPORT_SYMBOL(nw_gpio_modify_op);


unsignedintnw_gpio_read(void)

{

returninb(GP1_IO_BASE) | inb(GP2_IO_BASE) << 8;

}

EXPORT_SYMBOL(nw_gpio_read);


来看看混杂设备的设备号:

#ifndef_LINUX_MISCDEVICE_H

#define_LINUX_MISCDEVICE_H

#include<linux/module.h>

#include<linux/major.h>


/*

* Theseallocations are managed by device@lanana.org. If you use an

* entrythat is not in assigned your entry may well be moved and

* reassigned,or set dynamic if a fixed value is not justified.

*/


#definePSMOUSE_MINOR 1

#defineMS_BUSMOUSE_MINOR 2

#defineATIXL_BUSMOUSE_MINOR 3

/*#defineAMIGAMOUSE_MINOR 4 FIXME OBSOLETE */

#defineATARIMOUSE_MINOR 5

#defineSUN_MOUSE_MINOR 6

#defineAPOLLO_MOUSE_MINOR 7

#definePC110PAD_MINOR 9

/*#defineADB_MOUSE_MINOR 10 FIXME OBSOLETE */

#defineWATCHDOG_MINOR 130 /* Watchdog timer */

#defineTEMP_MINOR 131 /* Temperature Sensor */这里温度传感器

#defineRTC_MINOR 135这里是RTC的设备号.

#defineEFI_RTC_MINOR 136 /* EFI Time services */

#defineSUN_OPENPROM_MINOR 139

#defineDMAPI_MINOR 140 /* DMAPI */

#defineNVRAM_MINOR 144

#defineSGI_MMTIMER 153

#defineSTORE_QUEUE_MINOR 155

#defineI2O_MINOR 166

#defineMICROCODE_MINOR 184

#defineTUN_MINOR 200

#defineMWAVE_MINOR 219 /* ACP/Mwave Modem */

#defineMPT_MINOR 220

#defineMPT2SAS_MINOR 221

#defineUINPUT_MINOR 223

#defineHPET_MINOR 228

#defineFUSE_MINOR 229

#defineKVM_MINOR 232

#defineBTRFS_MINOR 234

#defineAUTOFS_MINOR 235

#defineMAPPER_CTRL_MINOR 236

#defineMISC_DYNAMIC_MINOR 255


structdevice;


structmiscdevice {

intminor;

constchar *name;

conststruct file_operations *fops;

structlist_head list;

structdevice *parent;

structdevice *this_device;

constchar *nodename;

mode_tmode;

};


externint misc_register(struct miscdevice * misc);

externint misc_deregister(struct miscdevice *misc);


#defineMODULE_ALIAS_MISCDEV(minor) \

MODULE_ALIAS("char-major-"__stringify(MISC_MAJOR) \

"-"__stringify(minor))

#endif





0 0
原创粉丝点击