基于S3C2440的嵌入式Linux驅(qū)動——SPI子系統(tǒng)解讀(三)
該系列文章將分為四個部分:
第一部分,將對SPI子系統(tǒng)整體進(jìn)行描述,同時給出SPI的相關(guān)數(shù)據(jù)結(jié)構(gòu),最后描述SPI總線的注冊?;赟3C2440的嵌入式Linux驅(qū)動——SPI子系統(tǒng)解讀(一)
第二部分,該文將對SPI的主控制器(master)驅(qū)動進(jìn)行描述?;赟3C2440的嵌入式Linux驅(qū)動——SPI子系統(tǒng)解讀(二)
第三部分,即本篇文章,該文將對SPI設(shè)備驅(qū)動,也稱protocol 驅(qū)動,進(jìn)行講解。
第四部分,通過SPI設(shè)備驅(qū)動留給用戶層的API,我們將從上到下描述數(shù)據(jù)是如何通過SPI的protocol 驅(qū)動,由bitbang中轉(zhuǎn),最后由master驅(qū)動將數(shù)據(jù)傳輸出
去?;赟3C2440的嵌入式Linux驅(qū)動——SPI子系統(tǒng)解讀(四)
本文屬于第三部分。
5. SPI設(shè)備驅(qū)動
在主控制器驅(qū)動中,spi_device已經(jīng)注冊了,在設(shè)備驅(qū)動中,首先要做的就是注冊spi_driver,并提供用戶層相應(yīng)的API。
5.1 SPI設(shè)備驅(qū)動的注冊
下列數(shù)據(jù)結(jié)構(gòu)及函數(shù)位于drivers/spi/spidev.c。
staticstructfile_operationsspidev_fops={
.owner=THIS_MODULE,
/*REVISITswitchtoaioprimitives,sothatuserspace
*getsmorecompleteAPIcoverage.It'llsimplifythings
*too,exceptforthelocking.
*/
.write=spidev_write,
.read=spidev_read,
.unlocked_ioctl=spidev_ioctl,
.open=spidev_open,
.release=spidev_release,
};
/*Themainreasontohavethisclassistomakemdev/udevcreatethe
*/dev/spidevB.CcharacterdevicenodesexposingouruserspaceAPI.
*Italsosimplifiesmemorymanagement.
*/
staticstructclass*spidev_class;
staticstructspi_driverspidev_spi={
.driver={
.name="spidev",
.owner=THIS_MODULE,
},
.probe=spidev_probe,
.remove=__devexit_p(spidev_remove),
/*NOTE:suspend/resumemethodsarenotnecessaryhere.
*Wedon'tdoanythingexceptpasstherequeststo/from
*theunderlyingcontroller.Therefrigeratorhandles
*mostissues;thecontrollerdriverhandlestherest.
*/
};
staticint__initspidev_init(void)
{
intstatus;
/*Claimour256reserveddevicenumbers.Thenregisteraclass
*thatwillkeyudev/mdevtoadd/remove/devnodes.Last,register
*thedriverwhichmanagesthosedevicenumbers.
*/
BUILD_BUG_ON(N_SPI_MINORS>256);/*檢查次設(shè)備號*/
status=register_chrdev(SPIDEV_MAJOR,"spi",&spidev_fops);/*注冊字符設(shè)備,major=153*/
if(status<0)
returnstatus;
spidev_class=class_create(THIS_MODULE,"spidev");/*創(chuàng)建spidev類*/
if(IS_ERR(spidev_class)){
unregister_chrdev(SPIDEV_MAJOR,spidev_spi.driver.name);
returnPTR_ERR(spidev_class);
}
status=spi_register_driver(&spidev_spi);/*注冊spi_driver,并調(diào)用probe方法*/
if(status<0){
class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR,spidev_spi.driver.name);
}
returnstatus;
}
module_init(spidev_init);
staticvoid__exitspidev_exit(void)
{
spi_unregister_driver(&spidev_spi);/*注銷spi_driver*/
class_destroy(spidev_class);/*注銷類*/
unregister_chrdev(SPIDEV_MAJOR,spidev_spi.driver.name);/*注銷字符設(shè)備*/
}
module_exit(spidev_exit);
該函數(shù)中,創(chuàng)建了一個字符設(shè)備以提供API給用戶層,同時創(chuàng)建了一個spidev類,最后注冊spi_driver到內(nèi)核中。
在這里我們看到了SPI設(shè)備驅(qū)動是如何提供API給用戶層的,那就是通過再熟悉不過的字符設(shè)備。通過字符設(shè)備,給用戶層提供了5個API:open,release,write,read和ioctl。本文在后面將介紹open和close,剩余3個將在本系列的第四篇文章中介紹。
接著看下spi_register_driver函數(shù), 該函數(shù)位于drivers/spi/spidev.c。
/**
*spi_register_driver-registeraSPIdriver
*@sdrv:thedrivertoregister
*Context:cansleep
*/
intspi_register_driver(structspi_driver*sdrv)
{
sdrv->driver.bus=&spi_bus_type;
if(sdrv->probe)
sdrv->driver.probe=spi_drv_probe;
if(sdrv->remove)
sdrv->driver.remove=spi_drv_remove;
if(sdrv->shutdown)
sdrv->driver.shutdown=spi_drv_shutdown;
returndriver_register(&sdrv->driver);
}
EXPORT_SYMBOL_GPL(spi_register_driver);
在調(diào)用driver_register的過程中,將用driver.name和spi_device的modalias字段進(jìn)行比較,兩者相等則將該spi_driver和spi_device進(jìn)行綁定。
當(dāng)spi_driver注冊成功以后,將調(diào)用probe方法:spidev_probe函數(shù)。
5.2 probe方法
我們來看看spidev_probe這個函數(shù),該函數(shù)位于drivers/spi/spidev.c。
#defineSPIDEV_MAJOR153/*assigned*/
#defineN_SPI_MINORS32/*...upto256*/
staticunsignedlongminors[N_SPI_MINORS/BITS_PER_LONG];/**/
staticLIST_HEAD(device_list);
staticDEFINE_MUTEX(device_list_lock);
staticintspidev_probe(structspi_device*spi)
{
structspidev_data*spidev;
intstatus;
unsignedlongminor;
/*Allocatedriverdata*/
spidev=kzalloc(sizeof(*spidev),GFP_KERNEL);/*以kmalloc分配內(nèi)存,并清0*/
if(!spidev)
return-ENOMEM;
/* Initialize the