1. 姝g偣鍘熷瓙宓屽叆寮弆inux椹卞姩寮鍙戔斺擫inux IIO椹卞姩
鍦ㄥ伐涓氱墿鑱旂綉鐨勬氮娼涓锛孡inux IIO椹卞姩寮鍙戝逛簬绠$悊ADC鍜孌AC绫讳紶鎰熷櫒鑷冲叧閲嶈併侷IO瀛愮郴缁燂紙Instrial I/O锛夋槸鍐呮牳璁捐$殑绮惧欑粍浠讹紝涓撲负杩欑被璁惧囨彁渚涢珮鏁堝拰鐏垫椿鐨勭$悊銆傞氳繃iio_dev缁撴瀯浣擄紝椹卞姩寮鍙戣呰兘澶熸搷鎺ц惧囨ā寮忋佺紦鍐插尯鍜屽氶氶亾淇℃伅锛岀‘淇濇暟鎹閲囬泦鐨勭簿鍑嗗拰瀹炴椂鎬с
瑕佸紑濮婭IO椹卞姩鐨勫紑鍙戯紝棣栧厛锛岄┍鍔ㄥ紑鍙戣呴渶瑕侀氳繃iio_device_alloc鍑芥暟鐢宠穒io_dev缁撴瀯锛屽苟鍒╃敤iio_priv灞炴ц幏鍙栬嚜瀹氫箟璁惧囩殑璇︾粏淇℃伅銆傛垚鍔熻幏鍙杋io_dev鍚庯紝瀹冭繑鍥炶惧囩殑棣栧湴鍧锛屽惁鍒欒繑鍥濶ULL銆傛帴涓嬫潵鐨勬ラゅ寘鎷鍒濆嬪寲銆佹敞鍐屽拰娉ㄩ攢iio_dev锛岀‘淇濊惧囩殑鐢熷懡鍛ㄦ湡绠$悊銆
绀轰緥浠g爜涓锛岄氳繃iio_device_alloc涓巌io_priv鐨勯厤鍚堬紝寮鍙戣呭彲浠ヨ交鏉剧敵璇峰苟鎻愬彇icm20608_dev缁撴瀯浣擄紝鐢ㄤ簬澶勭悊ICM20608杩欑被浼犳劅鍣ㄣ俰io_device_free鑷鍔ㄥ勭悊閲婃斁iio_dev锛屾棤闇鎵嬪姩鎿嶄綔銆傚湪椹卞姩涓锛屽叧閿鐨刬io_device_register鐢ㄤ簬璁惧囨敞鍐岋紝鑰宨io_device_unregister鍒欑敤浜庢敞閿璁惧囷紝纭淇濊祫婧愮殑姝g‘娓呯悊銆
娣卞叆鐞嗚Вiio_dev鐨刬nfo缁撴瀯浣撹嚦鍏抽噸瑕侊紝瀹冨畾涔夊湪iio.h涓锛屽寘鍚閫氱敤灞炴у拰璇诲啓鏁版嵁鎿嶄綔鎺ュ彛銆俽ead_raw鍜寃rite_raw鍑芥暟鏄鐢ㄦ埛绌洪棿涓庤惧囦氦浜掔殑鏍稿績锛屽厑璁哥洿鎺ヨ诲啓浼犳劅鍣ㄦ暟鎹锛屽弬鏁板寘鎷璁惧囥侀氶亾鍜屾暟鎹鍊笺傚悓鏃讹紝mask鐢ㄤ簬鎸囧畾璇诲彇鍐呭癸紝渚嬪傞檧铻轰华鍜屽姞閫熷害璁$殑鍘熷嬪笺佽寖鍥村拰鍒嗚鲸鐜囥
渚嬪傦紝ICM20608鎷ユ湁7涓閫氶亾锛屾瘡涓閫氶亾瀵瑰簲涓涓浼犳劅鍣ㄧ淮搴︼紝濡傚姞閫熷害璁$殑X銆乊銆乑杞淬傞氶亾鎻忚堪鐢眎io_chan_spec缁撴瀯浣撴壙杞斤紝鍖呮嫭绫诲瀷銆佺储寮曘佷慨楗扮︼紙濡侷IO_MOD_X锛夊拰鍦板潃绛夎︾粏淇℃伅銆傞氶亾閰嶇疆鏃讹紝濡傚湴鍧鐨勯夋嫨鍜屾壂鎻忕储寮曠殑璁剧疆锛屽睍绀轰簡IIO妗嗘灦瀵逛簬涓嶅悓绫诲瀷浼犳劅鍣ㄧ殑骞挎硾鏀鎸併
鍦ㄥ紑鍙戣繃绋嬩腑锛屽備笌SPI鎴朓IC閫氫俊锛屽彲鑳借繕闇缁撳悎regmap鎴杙latform椹卞姩銆備互SPI椹卞姩涓轰緥锛屽紑鍙戣呴渶瑕佺紪鍐檖robe鍜宺emove鍑芥暟锛屼互鍙婅繘琛岃惧囧尮閰嶅拰椹卞姩缁撴瀯浣撶殑閰嶇疆銆傚湪瀹為檯绀轰緥涓锛屼唬鐮56.2.1.1灞曠ず浜嗗備綍灏咺IO妗嗘灦涓嶴PI椹卞姩鏁村悎锛屽寘鎷璁惧囩殑鍒濆嬪寲銆佹敞鍐屽拰璧勬簮閲婃斁銆
鐢ㄦ埛鑷瀹氫箟璁惧囩粨鏋勪綋涓鍖呭惈indio_dev銆乧han鍜宮ask鍙傛暟锛屾寚瀵兼搷浣滅殑鎵ц屻侷IO閫氶亾鏁扮粍鍒欏畾涔変簡鏁版嵁璇诲啓鐨勯昏緫璺寰勩俰io_info缁撴瀯浣撲腑锛宺ead_raw銆亀rite_raw鍜寃rite_raw_get_fmt鎴愬憳鎻愪緵浜嗘暟鎹璇诲彇銆佸啓鍏ュ拰鏍煎紡鑾峰彇鐨勬帴鍙c
鍦⊿PI椹卞姩鐨刾robe鍑芥暟涓锛屽紑鍙戣呴渶閫氳繃devm_iio_device_alloc鑾峰彇iio_dev锛岄厤缃鐩稿叧纭浠舵帴鍙o紝濡俿pi_device鍜宺egmap锛屽垵濮嬪寲骞舵敞鍐孖IO璁惧囥傚悓鏃讹紝鑺鐗囩殑鍒濆嬪寲涔熸槸鍏抽敭姝ラゃ俽emove鍑芥暟鍒欏湪璁惧囩Щ闄ゆ椂璐熻矗娉ㄩ攢璁惧囧苟閲婃斁璧勬簮銆
閽堝笽CM20608 IIO椹卞姩寮鍙戯紝閫氳繃SPI鍜宺egmap锛屽畾涔変簡閫氶亾瀹忓拰鏋氫妇绫诲瀷锛屽寘鎷闄铻轰华銆佸姞閫熷害璁°佹俯搴﹀拰鏃堕棿鎴抽氶亾銆傛瀯寤篿cm20608_dev缁撴瀯浣撴椂锛屽寘鍚蹇呰佺殑閫氫俊鎺ュ彛鍜岄厤缃鍙傛暟銆傛牳蹇冮氶亾鐨勬弿杩伴氳繃iio_chan_spec鏁扮粍锛屽寘鍚7涓閫氶亾鐨勯厤缃锛屼互鍙婅诲啓瀵勫瓨鍣ㄥ拰鍒濆嬪寲閰嶇疆鐨勫嚱鏁般
椹卞姩鐨勫疄鐜颁腑锛宨cm20608_probe鍑芥暟璐熻矗璁惧囨敞鍐岋紝鍖呮嫭鍐呭瓨鍒嗛厤銆乮cm20608_dev缁撴瀯鐨勮剧疆銆丼PI鎺ュ彛鍜宺egmap閰嶇疆锛屼互鍙奍CM20608鐨勫垵濮嬪寲銆俰cm20608_remove鍒欏湪鍗歌浇鏃舵敞閿璁惧囧苟閲婃斁璧勬簮銆
鍦╯ysfs涓锛孖IO璁惧囨枃浠跺懡鍚嶈勫垯涓ヨ皑锛屽俰n_accel_x_raw锛屾寚绀哄姞閫熷害璁X杞达紝鍖呭惈浜嗛氶亾绫诲瀷鍜岀壒瀹氬睘鎬с傜敤鎴风┖闂撮氳繃icm20608_read_raw绛夊嚱鏁拌诲彇鏁版嵁锛岄氳繃iio_info鎺ュ彛鎿嶄綔锛屽傝剧疆浼犳劅鍣ㄨ寖鍥村拰璇诲彇鏁版嵁鏍煎紡銆
缂栧啓icm20608_read_raw鍑芥暟鏃讹紝瑕佽冭檻鍒颁笉鍚屼紶鎰熷櫒绫诲瀷鍜岄氶亾灞炴э紝渚嬪傞噺绋嬨佹牎鍑嗗肩瓑銆傝屽啓鍏ユ搷浣滃垯娑夊強icm20608_write_raw锛屾牴鎹鐢ㄦ埛杈撳叆鐨勬暟鎹杩涜岄厤缃銆
姝ゅ栵紝瑙﹀彂鍣ㄦ満鍒舵槸IIO椹卞姩涓鐨勯噸瑕佺粍鎴愰儴鍒嗭紝鐢ㄤ簬鏁版嵁閲囬泦鍜岀紦鍐插尯绠$悊銆傞氳繃iio_trigger鍜宨io_triggered_buffer_setup锛屽紑鍙戣呭彲浠ュ垱寤鸿嚜瀹氫箟瑙﹀彂鍣锛岄厤缃涓鏂澶勭悊锛屼互鍙婂湪涓鏂鍙戠敓鏃惰诲彇鏁版嵁銆
鎬荤粨鏉ヨ达紝linux IIO椹卞姩寮鍙戞秹鍙婅惧囩殑鍒濆嬪寲銆侀氫俊鎺ュ彛閰嶇疆銆侀氶亾绠$悊銆乻ysfs鎺ュ彛璁捐°佹暟鎹璇诲啓浠ュ強瑙﹀彂鍣ㄥ拰缂撳啿鍖虹殑璁剧疆銆傞氳繃杩欎簺姝ラわ紝椹卞姩寮鍙戣呭彲浠ュ疄鐜板瑰伐涓氱幆澧冧腑鍚勭嶄紶鎰熷櫒鐨勯珮鏁堢$悊鍜屾暟鎹閲囬泦锛屼负鐗╄仈缃戣惧囨彁渚涘己澶х殑鍔熻兘鏀鎸併
2. 如何系统的学习Linux驱动开发
在学习之前一直对驱动开发非常的陌生,感觉有点神秘。不知道驱动开发和普通的程序开发究竟有什么不同;它的基本框架又是什么样的;他的开发环境有什么特殊的地方;以及怎么写编写一个简单的字符设备驱动前编译加载,下面我就对这些问题一个一个的介绍。
一、驱动的基本框架
1.那么究竟什么是驱动程序,它有什么用呢:
l驱动是硬件设备与应用程序之间的一个中间软件层
l它使得某个特定硬件能够响应一个定义良好的内部编程接口,同时完全隐蔽了设备的工作细节
l用户通过一组与具体设备无关的标准化的调用来完成相应的操作
l驱动程序的任务就是把这些标准化的系统调用映射到具体设备对于实际硬件的特定操作上
l驱动程序是内核的一部分,可以使用中断、DMA等操作
l驱动程序在用户态和内核态之间传递数据
2.Linux驱动的基本框架
3.Linux下设备驱动程序的一般可以分为以下三类
1)字符设备
a)所有能够象字节流一样访问的设备都通过字符设备来实现
b)它们被映射为文件系统中的节点,通常在/dev/目录下面
c)一般要包含open read write close等系统调用的实现
2)块设备
d)通常是指诸如磁盘、内存、Flash等可以容纳文件系统的存储设备。
e)块设备也是通过文件系统来访问,与字符设备的区别是:内核管理数据的方式不同
f)它允许象字符设备一样以字节流的方式来访问,也可一次传递任意多的字节。
3)网络接口设备
g)通常它指的是硬件设备,但有时也可能是一个软件设备(如回环接口loopback),它们由内核中网络子系统驱动,负责发送和接收数据包。
h)它们的数据传送往往不是面向流的,因此很难将它们映射到一个文件系统的节点上。
二、怎么搭建一个驱动的开发环境
因为驱动是要编译进内核,在启动内核时就会驱动此硬件设备;或者编译生成一个.o文件,当应用程序需要时再动态加载进内核空间运行。因此编译任何一个驱动程序都要链接到内核的源码树。所以搭建环境的第一步当然是建内核源码树
1.怎么建内核源码树
a)首先看你的系统有没有源码树,在你的/lib/ moles目录下会有内核信息,比如我当前的系统里有两个版本:
#ls /lib/ moles
2.6.15-rc72.6.21-1.3194.fc7
查看其源码位置:
## ll /lib/moles/2.6.15-rc7/build
lrwxrwxrwx 1 root root 27 2008-04-28 19:19 /lib/moles/2.6.15-rc7/build -> /root/xkli/linux-2.6.15-rc7
发现build是一个链接文件,其所对应的目录就是源码树的目录。但现在这里目标目录已经是无效的了。所以得自己重新下载
b)下载并编译源码树
有很多网站上可以下载,但官方网址是:
http://www.kernel.org/pub/linux/kernel/v2.6/
下载完后当然就是解压编译了
# tar –xzvf linux-2.6.16.54.tar.gz
#cd linux-2.6.16.54
## make menuconfig (配置内核各选项,如果没有配置就无法下一步编译,这里可以不要改任何东西)
#make
…
如果编译没有出错。那么恭喜你。你的开发环境已经搭建好了
三、了解驱动的基本知识
1.设备号
1)什么是设备号呢?我们进系统根据现有的设备来讲解就清楚了:
#ls -l /dev/
crwxrwxrwx 1 root root1,3 2009-05-11 16:36 null
crw------- 1 root root4,0 2009-05-11 16:35 systty
crw-rw-rw- 1 root tty5,0 2009-05-11 16:36 tty
crw-rw---- 1 root tty4,0 2009-05-11 16:35 tty0
在日期前面的两个数(如第一列就是1,3)就是表示的设备号,第一个是主设备号,第二个是从设备号
2)设备号有什么用呢?
l传统上,主编号标识设备相连的驱动.例如, /dev/null和/dev/zero都由驱动1来管理,而虚拟控制台和串口终端都由驱动4管理
l次编号被内核用来决定引用哪个设备.依据你的驱动是如何编写的自己区别
3)设备号结构类型以及申请方式
l在内核中, dev_t类型(在中定义)用来持有设备编号,对于2.6.0内核, dev_t是32位的量, 12位用作主编号, 20位用作次编号.
l能获得一个dev_t的主或者次编号方式:
MAJOR(dev_t dev); //主要
MINOR(dev_t dev);//次要
l但是如果你有主次编号,需要将其转换为一个dev_t,使用: MKDEV(int major, int minor);
4)怎么在程序中分配和释放设备号
在建立一个字符驱动时需要做的第一件事是获取一个或多个设备编号来使用.可以达到此功能的函数有两个:
l一个是你自己事先知道设备号的
register_chrdev_region,在中声明:
int register_chrdev_region(dev_t first, unsigned int count, char *name);
first是你要分配的起始设备编号. first的次编号部分常常是0,count是你请求的连续设备编号的总数. name是应当连接到这个编号范围的设备的名子;它会出现在/proc/devices和sysfs中.
l第二个是动态动态分配设备编号
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
使用这个函数, dev是一个只输出的参数,它在函数成功完成时持有你的分配范围的第一个数. fisetminor应当是请求的第一个要用的次编号;它常常是0. count和name参数如同给request_chrdev_region的一样.
5)设备编号的释放使用
不管你是采用哪些方式分配的设备号。使用之后肯定是要释放的,其方式如下:
void unregister_chrdev_region(dev_t first, unsigned int count);
6)
2.驱动程序的二个最重要数据结构
1)file_operation
倒如字符设备scull的一般定义如下:
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.llseek = scull_llseek,
.read = scull_read,
.write = scull_write,
.ioctl = scull_ioctl,
.open = scull_open,
.release = scull_release,
};
file_operation也称为设备驱动程序接口
定义在,是一个函数指针的集合.每个打开文件(内部用一个file结构来代表)与它自身的函数集合相关连(通过包含一个称为f_op的成员,它指向一个file_operations结构).这些操作大部分负责实现系统调用,因此,命名为open, read,等等
2)File
定义位于include/fs.h
struct file结构与驱动相关的成员
lmode_t f_mode标识文件的读写权限
lloff_t f_pos当前读写位置
lunsigned int_f_flag文件标志,主要进行阻塞/非阻塞型操作时检查
lstruct file_operation * f_op文件操作的结构指针
lvoid * private_data驱动程序一般将它指向已经分配的数据
lstruct dentry* f_dentry文件对应的目录项结构
3.字符设备注册
1)内核在内部使用类型struct cdev的结构来代表字符设备.在内核调用你的设备操作前,必须编写分配并注册一个或几个这些结构.有2种方法来分配和初始化一个这些结构.
l如果你想在运行时获得一个独立的cdev结构,可以这样使用:
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &my_fops;
l如果想将cdev结构嵌入一个你自己的设备特定的结构;你应当初始化你已经分配的结构,使用:
void cdev_init(struct cdev *cdev, struct file_operations *fops);
2)一旦cdev结构建立,最后的步骤是把它告诉内核,调用:
int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
说明:dev是cdev结构, num是这个设备响应的第一个设备号, count是应当关联到设备的设备号的数目.常常count是1,但是有多个设备号对应于一个特定的设备的情形.
3)为从系统去除一个字符设备,调用:
void cdev_del(struct cdev *dev);
4.open和release