blackberrypi模組spi驅動分析(1)

2021-06-15 08:14:35 字數 4050 閱讀 3808

下面我們將分析spidev是如何處理ioctl傳送的spi訊息的。blackberry的kernel可以從獲得。

git init

git fetch git: rpi-3.6.y:refs/remotes/origin/rpi-3.6.y

git checkout rpi-3.6.y

raspbian的spi驅動由兩個模組組成,乙個是spidev, 另乙個是bcm2708-spi。spidev依賴bcm2708-spi提供的功能註冊乙個字元裝置,提供類似oopen, write, ioctl的操作

static const struct file_operations spidev_fops = ;

spi驅動主要涉及一下幾個檔案

/linux/include/spi/spi.h

/drivers/spi/spidev.c

/drivers/spi/spi.c

/drivers/spi/bcm2708-spi.c

/arch/arm/mach-bcm2708/bcm2708.c

主要涉及的結構有

spidev_data

spi_master

spi_device

下面我們通過跟蹤乙個ioctl message怎麼從spidev傳遞到bcm2708-spi來看spi驅動是如何工作的。我們先看怎麼樣向blackberrypi的spi裝置傳送訊息。

當spidev初次裝載時,核心會我們呼叫spidev_init

static int __init spidev_init(void)

status = spi_register_driver(&spidev_spi_driver);

if (status < 0)

return status;

}

spidev在初始化時先註冊乙個字元裝置,再建立乙個叫spidev的class,然後呼叫spi_register_driver在spi_bus_type上註冊乙個驅動程式。當我們開啟這個字元裝置時,就會呼叫spidev_open,spidev_open會為當前device尋找乙個spidev_data結構體,這個結構體是在驅動的spidev_probe被呼叫時建立的。

static int spidev_open(struct inode *inode, struct file *filp)

} if (status == 0)

} if (status == 0)

} else

pr_debug("spidev: nothing for minor %d\n", iminor(inode));

mutex_unlock(&device_list_lock);

return status;

}

這個spidev_data其實是用來存放ioctl訊息的乙個快取,當使用者呼叫ioctl時同時傳遞的訊息就會被拷貝到這個記憶體空間裡。spidev_data中的buffer就是這個快取空間。

struct spidev_data ;

使用者使用ioctl向spi傳送或讀取訊息,最終就是呼叫spidev_ioctl。我們這裡省去了裝置設定相關的ioctl命令,只關注訊息的傳送和接受,也就是switch的default部分。spi_ioctl分配了乙個spi_message物件,將該訊息物件和使用者給出的spi_ioc_transfer傳給spidev_message函式。

static long

spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

tmp = _ioc_size(cmd);

if ((tmp % sizeof(struct spi_ioc_transfer)) != 0)

n_ioc = tmp / sizeof(struct spi_ioc_transfer);

if (n_ioc == 0)

break;

/* copy into scratch area */

ioc = kmalloc(tmp, gfp_kernel);

if (!ioc)

if (__copy_from_user(ioc, (void __user *)arg, tmp))

/* translate to spi_message, execute */

retval = spidev_message(spidev, ioc, n_ioc);

kfree(ioc);

break;

} mutex_unlock(&spidev->buf_lock);

spi_dev_put(spi);

return retval;

}

下面再看spidev_message繼續做什麼處理。spidev_message將使用者空間的訊息內容spi_ioc_transfer::tx_buf拷貝到spidev_data::buffer指向的核心空間中,並將spi_ioc_transfer轉換成spi_transfer,並將spi_transfer乙個乙個地加入到spi_message的乙個list成員transfers裡。然後再呼叫spidev_sync()。等spidev_sync返回後,又將spidev_data::buf的內容拷貝回spi_ioc_transfer::rx_buf。如果我們沒有猜錯spidev_sync肯定是通知乙個workqueue去處理spi_message中的內容。

static int spidev_message(struct spidev_data *spidev,

struct spi_ioc_transfer *u_xfers, unsigned n_xfers)

if (u_tmp->rx_buf)

if (u_tmp->tx_buf)

buf += k_tmp->len;

k_tmp->cs_change = !!u_tmp->cs_change;

k_tmp->bits_per_word = u_tmp->bits_per_word;

k_tmp->delay_usecs = u_tmp->delay_usecs;

k_tmp->speed_hz = u_tmp->speed_hz;

#ifdef verbose

dev_dbg(&spidev->spi->dev,

" xfer len %zd %s%s%s%dbits %u usec %uhz\n",

u_tmp->len,

u_tmp->rx_buf ? "rx " : "",

u_tmp->tx_buf ? "tx " : "",

u_tmp->cs_change ? "cs " : "",

u_tmp->bits_per_word ? : spidev->spi->bits_per_word,

u_tmp->delay_usecs,

u_tmp->speed_hz ? : spidev->spi->max_speed_hz);

#endif

spi_message_add_tail(k_tmp, &msg);

} status = spidev_sync(spidev, &msg);

if (status < 0)

goto done;

/* copy any rx data out of bounce buffer */

buf = spidev->buffer;

for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++)

} buf += u_tmp->len;

} status = total;

done:

kfree(k_xfers);

return status;

}

以上就是使用ioctl傳送spi訊息時的大體流程。我們還有乙個迷沒有解開,就是spidev_sync是怎麼處理spi_message的。我們下次繼續分析。

模組與包的S設計

理論可見上文模組載入方案 這次 下如何設計 api 使用 js 模擬其他語言常見的模組 與包 的概念 模組化實踐 google doc 1.新增模組 通過 s.add 向執行環境新增模組,模組可以新增乙個簡單物件,或者乙個載入函式。簡單物件 s.add module1 載入函式 s.add modu...

Python 時間模組 s 基本操作

年 月 日 時 分 秒 import datetime as dt 用於對 時間 的處理 today dt.date.today 今天的日期 print today type today 日期和型別 建立乙個日期 someday dt.date 2020,10,10 獲取當前日期的準確時間 toda...

MU709S 2模組的FTP使用

mu709s是wcdma的通訊模組,控制是通過at命令來執行的,具體的命令參考mudule at command手冊。關於ftp的連線首先要了解tcp ip協議族 1 其中比較重要的有slip協議 ppp協議 ip協議 icmp協議 arp協議 tcp協議 udp協議 ftp協議 dns協議 smt...