分析完i2c-core後,其實還遺留了不少問題,比如algo的掛載,I2C的ioctl命令的響應等,帶着這些問題,我們繼續分析i2c-dev.c的代碼,看看能否找到一些答案
自旋鎖和互斥鎖:這兩個鎖很相似,只是自旋鎖在等待的時候不會進入睡眠,而會一直佔用CPU,這樣會使得資料浪費嚴重,所以要慎用
小結:如果之前有接觸過字符型驅動程序的朋友都知道,字符型驅動設備都會爲設備關聯上open,read等操作函數來給應用層使用,而我們在前兩章內容分析的時候,卻沒有發現I2C設備有關聯這些操作函數,原來這些操作函數的實現是在這裏。
系統是將I2C總線看成了一個設備,而I2C總線上掛載的設備則需要分享這些命令。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
同樣的,先是準備工作,看下變量的定義等:
struct i2c_dev {
struct list_head list;
struct i2c_adapter *adap; //適配器
struct device *dev; //設備
};
#define I2C_MINORS 256 //設備號
static LIST_HEAD(i2c_dev_list); //設備鏈表
static DEFINE_SPINLOCK(i2c_dev_list_lock); //自旋鎖
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//這個函數名已經註釋了這個函數的作用了:根據index獲取設備信息
static struct i2c_dev *i2c_dev_get_by_minor(unsigned index)
{
struct i2c_dev *i2c_dev;
spin_lock(&i2c_dev_list_lock);
//遍歷i2c_dev_list鏈表,取出結構體i2c_dev
list_for_each_entry(i2c_dev, &i2c_dev_list, list) {
if (i2c_dev->adap->nr == index) //適配器的nr號爲指定的索引
goto found;
}
i2c_dev = NULL;
found:
spin_unlock(&i2c_dev_list_lock);
return i2c_dev;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//獲取一個空的i2c_dev宿主結構體掛載adapter,並將該設備掛載到i2c_dev_list上
static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
//索引號過大,出錯
if (adap->nr >= I2C_MINORS) {
printk(KERN_ERR "i2c-dev: Out of device minors (%d)\n",
adap->nr);
return ERR_PTR(-ENODEV);
}
//申請一塊內存,並關聯上輸入的adap信息
i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL);
if (!i2c_dev)
return ERR_PTR(-ENOMEM);
i2c_dev->adap = adap;
spin_lock(&i2c_dev_list_lock);
list_add_tail(&i2c_dev->list, &i2c_dev_list); //將設備鏈表掛載到i2c_dev_list上
spin_unlock(&i2c_dev_list_lock);
return i2c_dev;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//刪除一個設備
static void return_i2c_dev(struct i2c_dev *i2c_dev)
{
spin_lock(&i2c_dev_list_lock);
list_del(&i2c_dev->list); //將設備從鏈表中斷開
spin_unlock(&i2c_dev_list_lock);
kfree(i2c_dev); //釋放設備所佔用的內存
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//顯示適配器的名字
static ssize_t show_adapter_name(
struct device *dev, //設備指針
struct device_attribute *attr, //設備屬性
char *buf) //緩存
{
//通過device結構體的註解我們可以看到,devt成員是在建立文件系統的時候創建的
struct i2c_dev *i2c_dev = i2c_dev_get_by_minor(MINOR(dev->devt));
if (!i2c_dev)
return -ENODEV;
return sprintf(buf, "%s\n", i2c_dev->adap->name);
}
static DEVICE_ATTR(name, S_IRUGO, show_adapter_name, NULL);
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
可見,這裏是創建了一個dev_attr_name的設備屬性結構體
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
這裏開始是對I2C設備的操作代碼,應該是應用層的操作了
//I2C設備讀
static ssize_t i2cdev_read (
struct file *file, //文件句柄
char __user *buf, //操作緩存
size_t count, //操作字節數量
loff_t *offset) //偏移地址
{
char *tmp;
int ret;
//I2C設備的私有數據爲client結構體
struct i2c_client *client = (struct i2c_client *)file->private_data;
//操作字節不能大於8K
if (count > 8192)
count = 8192;
//申請一塊內存放操作數據
tmp = kmalloc(count, GFP_KERNEL);
if (tmp==NULL)
return -ENOMEM;
pr_debug("i2c-dev: i2c-%d reading %zd bytes.\n",
iminor(file->f_path.dentry->d_inode), count);
//調用i2c-core.c中的函數實現讀操作
ret = i2c_master_recv(client,tmp,count);
if (ret >= 0) //讀成功,將數據複製到用戶buf中
ret = copy_to_user(buf, tmp, count)?-EFAULT:ret;
kfree(tmp); //釋放內存
return ret;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//I2C設備的寫操作
//代碼基本和讀操作類似,只是調用的實現函數是i2c_master_send
//因爲代碼類似,所以這裏就刪去了些不重要的代碼
static ssize_t i2cdev_write (
struct file *file,
const char __user *buf,
size_t count,
loff_t *offset)
{
struct i2c_client *client = (struct i2c_client *)file->private_data;
tmp = kmalloc(count,GFP_KERNEL);
if (copy_from_user(tmp,buf,count)) {
kfree(tmp);
return -EFAULT;
}
ret = i2c_master_send(client,tmp,count);
kfree(tmp);
return ret;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//檢查addr地址是否已經存在於adapter中,不存在則返回0
//在分析i2c-core.c時就已經有相同功能的函數了,所以這裏就不再詳細分析了
static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr)
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
static int i2cdev_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
struct i2c_client *client = (struct i2c_client *)file->private_data;
struct i2c_rdwr_ioctl_data rdwr_arg;
struct i2c_smbus_ioctl_data data_arg;
union i2c_smbus_data temp;
struct i2c_msg *rdwr_pa; //SMBUS的數據結構體
u8 __user **data_ptrs;
int i,datasize,res;
unsigned long funcs;
//打印信息到設備的LOG中
dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
cmd, arg);
//根據不同的命令做響應
switch ( cmd ) {
//設置要操作的I2C設備地址
//由於在這個命令下,arg代表着I2C設備的地址,一般I2C設備地址爲7位,所以arg值不應該大於0x7f。如果設備帶有I2C_M_TEN參數,則說明I2C設備地址是10位,所以此時arg也不應該大於0x3ff
case I2C_SLAVE:
case I2C_SLAVE_FORCE:
if ((arg > 0x3ff) || //參數 >0x3ff
//設備屬性不帶I2C_M_TEN時,參數>0x7f
(((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
return -EINVAL; //出錯
if (cmd == I2C_SLAVE
&& i2cdev_check_addr(client->adapter, arg)) //設備地址(arg)不存在
return -EBUSY;
//將client的地址設置爲傳入的設備地址
//由於函數的一開始client是指向file->private_data的,所以這一步操作就相當於把file的地址設置爲了arg地址了
client->addr = arg;
return 0;
//設置設備地址是7位還是10位(I2C_M_TEN)
case I2C_TENBIT:
if (arg)
client->flags |= I2C_M_TEN;
else
client->flags &= ~I2C_M_TEN;
return 0;
//設置I2C傳輸的數據是否需要帶CRC校驗
//關於這裏的CRC校驗,實際就是在分析i2c-core.c文件中最後幾個函數所用的crc算法,用來保護傳輸的數據是否正確
case I2C_PEC:
if (arg)
client->flags |= I2C_CLIENT_PEC;
else
client->flags &= ~I2C_CLIENT_PEC;
return 0;
//獲取適配器支持的操作
case I2C_FUNCS:
//獲取adapter支持的操作
funcs = i2c_get_functionality(client->adapter);
return put_user(funcs, (unsigned long __user *)arg); //將數據傳給arg
//設備的讀寫操作,注意傳入參數的類型
case I2C_RDWR:
if (copy_from_user(&rdwr_arg,
(struct i2c_rdwr_ioctl_data __user *)arg,
sizeof(rdwr_arg)))
return -EFAULT;
//要操作的數據包(msg包)超過最大限制
if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
return -EINVAL;
//申請內存
rdwr_pa = (struct i2c_msg *)
kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),
GFP_KERNEL);
if (rdwr_pa == NULL) return -ENOMEM; //申請失敗
//將要操作的數據用用戶層複製到內核層
if (copy_from_user(rdwr_pa, rdwr_arg.msgs,
rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {
kfree(rdwr_pa);
return -EFAULT;
}
//申請一組指針,每一個元素指向一個msg包
data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);
if (data_ptrs == NULL) {
kfree(rdwr_pa);
return -ENOMEM;
}
res = 0;
for( i=0; i<rdwr_arg.nmsgs; i++ ) {
if ((rdwr_pa[i].len > 8192) || //要操作的數據不能大於8K
(rdwr_pa[i].flags & I2C_M_RECV_LEN)) {
res = -EINVAL;
break;
}
data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf; //指針和用戶層數據緩存關聯
//根據長度申請內存,注意,此時buf地址關聯上的是新申請的內存的地址
rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);
if(rdwr_pa[i].buf == NULL) {
res = -ENOMEM;
break;
}
//將要操作的用戶數據COPY到內核緩存中
//如果是讀操作,那麼這裏複製的數據是沒有意義的
//如果是寫操作,那麼這裏就將要寫入的數據給複製進了緩存中了
if(copy_from_user(rdwr_pa[i].buf,
data_ptrs[i],
rdwr_pa[i].len)) {
++i;
res = -EFAULT;
break;
}
} //for結束
//如果以上操作失敗(一般是申請內存失敗),則釋放內存並返回
if (res < 0) {
int j;
for (j = 0; j < i; ++j)
kfree(rdwr_pa[j].buf);
kfree(data_ptrs);
kfree(rdwr_pa);
return res;
}
//傳輸數據
res = i2c_transfer(client->adapter,
rdwr_pa,
rdwr_arg.nmsgs);
while(i-- > 0) {
//如果是讀操作,則把數據複製出來
if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD)) {
if(copy_to_user(
data_ptrs[i],
rdwr_pa[i].buf,
rdwr_pa[i].len)) {
res = -EFAULT;
}
}
kfree(rdwr_pa[i].buf); //釋放內存
}
kfree(data_ptrs);
kfree(rdwr_pa);
return res;
//SMBUS格式的讀寫操作
//實際上就是直接調用i2c-core.c中的i2c_smbus_xfer函數來實現I2C通訊
case I2C_SMBUS:
if (copy_from_user(&data_arg,
(struct i2c_smbus_ioctl_data __user *) arg,
sizeof(struct i2c_smbus_ioctl_data)))
return -EFAULT;
//這裏省略了很多參數合法性判斷的代碼
//當所有參數都判斷合法之後,則調用這個核心函數來實現對應的功能
res = i2c_smbus_xfer(client->adapter,client->addr,client->flags,
data_arg.read_write,
data_arg.command,data_arg.size,&temp);
if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
(data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||
(data_arg.read_write == I2C_SMBUS_READ))) {
if (copy_to_user(data_arg.data, &temp, datasize))
return -EFAULT;
}
return res;
//設置兩個參數
case I2C_RETRIES:
client->adapter->retries = arg;
break;
case I2C_TIMEOUT:
client->adapter->timeout = arg;
break;
default:
return -ENOTTY;
}
return 0;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//打開設備
static int i2cdev_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode); //獲取設備的次版本號
struct i2c_client *client;
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
i2c_dev = i2c_dev_get_by_minor(minor); //根據次版本號在i2c_dev_list中獲取設備
if (!i2c_dev)
return -ENODEV;
//根據關聯的適配器號獲取適配器,獲取的同時,該適配器的引用計數加1
adap = i2c_get_adapter(i2c_dev->adap->nr);
if (!adap)
return -ENODEV;
//申請一塊內存,準備掛載設備到文件的私有數據上
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
i2c_put_adapter(adap); //適配器的引用計數減1
return -ENOMEM;
}
snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
client->driver = &i2cdev_driver; //將i2cdev_driver掛載到設備驅動上
client->adapter = adap; //將適配器掛載上去
file->private_data = client; //文件的私有數據指向這個設備
return 0;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//釋放設備
static int i2cdev_release(struct inode *inode, struct file *file)
{
struct i2c_client *client = file->private_data;
i2c_put_adapter(client->adapter); //適配器的引用計數減1
kfree(client); //釋放設備client內存
file->private_data = NULL; //文件的私有數據指向空
return 0;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//這個結構就比較熟悉了,字符型驅動的操作函數
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE, //所有者
.llseek = no_llseek, //操作位置定位
.read = i2cdev_read, //讀操作
.write = i2cdev_write, //寫操作
.ioctl = i2cdev_ioctl, //IOCTL操作
.open = i2cdev_open, //打開文件
.release = i2cdev_release, //釋放文件
};
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
static struct class *i2c_dev_class;
//適配器探測
static int i2cdev_attach_adapter(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
int res;
i2c_dev = get_free_i2c_dev(adap); //申請一塊內存做i2c_dev並關聯上adapter
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);
//創建設備並掛載到結構體上
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr),
"i2c-%d", adap->nr);
if (IS_ERR(i2c_dev->dev)) {
res = PTR_ERR(i2c_dev->dev);
goto error;
}
//根據設備的屬性創建文件
//這一步應該就是將設備變成文件放到根目錄中了
res = device_create_file(i2c_dev->dev, &dev_attr_name);
if (res)
goto error_destroy;
pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
adap->name, adap->nr);
return 0;
//創建文件出錯
error_destroy:
//銷燬設備
device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
//創建設備出錯
error:
return_i2c_dev(i2c_dev); //將函數一開始創建的i2c_dev結構體釋放
return res;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//adapter斷開時執行的函數
static int i2cdev_detach_adapter(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
i2c_dev = i2c_dev_get_by_minor(adap->nr); //根據次版本號獲取設備指針
if (!i2c_dev)
return 0;
device_remove_file(i2c_dev->dev, &dev_attr_name); //刪除文件
return_i2c_dev(i2c_dev); //將i2c_dev佔的內存釋放
device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); //設備銷燬
pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name);
return 0;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//設備client卸載時調用的函數(什麼也沒幹)
static int i2cdev_detach_client(struct i2c_client *client)
{
return 0;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//設備驅動
static struct i2c_driver i2cdev_driver = {
.driver = {
.name = "dev_driver",
},
.id = I2C_DRIVERID_I2CDEV,
.attach_adapter = i2cdev_attach_adapter,
.detach_adapter = i2cdev_detach_adapter,
.detach_client = i2cdev_detach_client,
};
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//模塊的加載函數
module_init(i2c_dev_init);
module_exit(i2c_dev_exit);
//代碼在初始化段中,將在開機時自動執行
static int __init i2c_dev_init(void)
{
int res;
//代碼看到這裏的時候,順便看了一眼我的Linux啓動時打印出來的LOG,果然找到了這句
printk(KERN_INFO "i2c /dev entries driver\n");
//用主設備號註冊設備
res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
if (res)
goto out;
//創建設備類
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
if (IS_ERR(i2c_dev_class))
goto out_unreg_chrdev;
res = i2c_add_driver(&i2cdev_driver); //註冊設備驅動
if (res)
goto out_unreg_class;
return 0;
//註冊失敗
out_unreg_class:
class_destroy(i2c_dev_class); //銷燬設備類
out_unreg_chrdev:
unregister_chrdev(I2C_MAJOR, "i2c"); //根據主設備號卸載設備
out:
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
return res;
}
static void __exit i2c_dev_exit(void)
{
i2c_del_driver(&i2cdev_driver); //刪除驅動
class_destroy(i2c_dev_class); //銷燬設備類
unregister_chrdev(I2C_MAJOR,"i2c"); //根據主設備號卸載設備
}