天天看點

【轉載】Linux裝置驅動程式

(本部分的一些示例源碼來自drivers/usb/usb-skeleton.c,它是Linux核心為我們提供的最基礎的USB驅動程式,USB骨架程式) 驅動程式把驅動對象注冊到 USB 子系統中,之後使用供應商(idVendor)和裝置(idProduct)辨別來判斷對應的硬體是否已經安裝. 驅動的裝置支援清單 struct usb_device_id 結構提供了這個驅動支援的不同類型 USB 裝置的清單. USB 核心通過此清單用來決定裝置對應的驅動,熱插拔腳本也通過此清單來決定當特定裝置被插入系統時,應該自動加載的驅動.

struct usb_device_id {

    __u16        match_flags;

    __u16        idVendor;//USB裝置的産品ID,有制造商自定

    __u16        bcdDevice_lo;

    __u16        bcdDevice_hi;

    __u8        bDeviceClass;

    __u8        bDeviceSubClass;

    __u8        bDeviceProtocol;

    __u8        bInterfaceClass;

    __u8        bInterfaceSubClass;

    __u8        bInterfaceProtocol;

    kernel_ulong_t    driver_info;

};

//以上結構體中__u16 match_flags;所使用的define:

//include/linux/mod_devicetable.h

#define USB_DEVICE_ID_MATCH_VENDOR        0x0001

#define USB_DEVICE_ID_MATCH_PRODUCT        0x0002

#define USB_DEVICE_ID_MATCH_DEV_LO        0x0004

#define USB_DEVICE_ID_MATCH_DEV_HI        0x0008

#define USB_DEVICE_ID_MATCH_DEV_CLASS        0x0010

#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS    0x0020

#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL    0x0040

#define USB_DEVICE_ID_MATCH_INT_CLASS        0x0080

#define USB_DEVICE_ID_MATCH_INT_SUBCLASS    0x0100

#define USB_DEVICE_ID_MATCH_INT_PROTOCOL    0x0200

//include/linux/usb.h

#define USB_DEVICE_ID_MATCH_DEVICE /

(USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT)

#define USB_DEVICE_ID_MATCH_DEV_RANGE /

(USB_DEVICE_ID_MATCH_DEV_LO | USB_DEVICE_ID_MATCH_DEV_HI)

#define USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION /

(USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_RANGE)

#define USB_DEVICE_ID_MATCH_DEV_INFO /

(USB_DEVICE_ID_MATCH_DEV_CLASS | /

        USB_DEVICE_ID_MATCH_DEV_SUBCLASS | /

        USB_DEVICE_ID_MATCH_DEV_PROTOCOL)

#define USB_DEVICE_ID_MATCH_INT_INFO /

(USB_DEVICE_ID_MATCH_INT_CLASS | /

        USB_DEVICE_ID_MATCH_INT_SUBCLASS | /

        USB_DEVICE_ID_MATCH_INT_PROTOCOL)

//這個結構體一般不用手動指派,以下的宏可以實作指派:

//僅和指定的制造商和産品ID比對,用于需要特定驅動的裝置

#define USB_DEVICE(vend,prod) /

.match_flags = USB_DEVICE_ID_MATCH_DEVICE, /

.idVendor = (vend), /

.idProduct = (prod)

//僅和某版本範圍内的指定的制造商和産品ID比對

#define USB_DEVICE_VER(vend, prod, lo, hi) /

.match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, /

.idVendor = (vend), /

.idProduct = (prod), /

.bcdDevice_lo = (lo), /

.bcdDevice_hi = (hi)

//僅和指定的接口協定、制造商和産品ID比對

#define USB_DEVICE_INTERFACE_PROTOCOL(vend, prod, pr) /

.match_flags = USB_DEVICE_ID_MATCH_DEVICE | /

         USB_DEVICE_ID_MATCH_INT_PROTOCOL, /

.idVendor = (vend), /

.idProduct = (prod), /

.bInterfaceProtocol = (pr)

//僅和指定的裝置類型相比對

#define USB_DEVICE_INFO(cl, sc, pr) /

.match_flags = USB_DEVICE_ID_MATCH_DEV_INFO, /

.bDeviceClass = (cl), /

.bDeviceSubClass = (sc), /

.bDeviceProtocol = (pr)

//僅和指定的接口類型相比對

#define USB_INTERFACE_INFO(cl, sc, pr) /

.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, /

.bInterfaceClass = (cl), /

.bInterfaceSubClass = (sc), /

.bInterfaceProtocol = (pr)

//僅和指定的制造商、産品ID和接口類型相比對

#define USB_DEVICE_AND_INTERFACE_INFO(vend, prod, cl, sc, pr) /

.match_flags = USB_DEVICE_ID_MATCH_INT_INFO /

| USB_DEVICE_ID_MATCH_DEVICE, /

.idVendor = (vend), /

.idProduct = (prod), /

.bInterfaceClass = (cl), /

.bInterfaceSubClass = (sc), /

.bInterfaceProtocol = (pr)

對于一個隻為一個供應商的一個 USB 裝置的簡單 USB 裝置驅動, 其 struct usb_device_id 可定義如下:

#define USB_SKEL_VENDOR_ID    0xfff0

#define USB_SKEL_PRODUCT_ID    0xfff0

static struct usb_device_id skel_table [] = {

{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },

{ }

};

MODULE_DEVICE_TABLE(usb, skel_table);

MODULE_DEVICE_TABLE 宏是必需的,它允許使用者空間工具判斷該驅動可控制什麼裝置. 對于 USB 驅動, 這個宏中的第一個值必須是 usb . 如果你需要這個驅動被系統中每個 USB 裝置調用, 建立僅需設定 driver_info 成員:

static struct usb_device_id usb_ids[] = {

{.driver_info = 42},

{}

};

注冊USB驅動程式 所有 USB 驅動都必須建立的主要結構是 struct usb_driver. 這個結構必須被 USB 驅動程式手動填充并且包含多個回調函數和變量, 并向 USB 核心描述 USB 驅動程式:

struct usb_driver {

const char *name;

int (*probe) (struct usb_interface *intf,

         const struct usb_device_id *id);

void (*disconnect) (struct usb_interface *intf);

int (*ioctl) (struct usb_interface *intf, unsigned int code,

void *buf);

int (*suspend) (struct usb_interface *intf, pm_message_t message);

int (*resume) (struct usb_interface *intf);

int (*reset_resume)(struct usb_interface *intf);

int (*pre_reset)(struct usb_interface *intf);

int (*post_reset)(struct usb_interface *intf);

const struct usb_device_id *id_table;

struct usb_dynids dynids;

struct usbdrv_wrap drvwrap;

unsigned int no_dynamic_id:1;

unsigned int supports_autosuspend:1;

unsigned int soft_unbind:1;

};

#define    to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)

建立一個簡單的 struct usb_driver 結構, 隻有 4 個成員需要初始化:

static struct usb_driver skel_driver = {

.name = "skeleton",

.id_table = skel_table,

.probe = skel_probe,

.disconnect = skel_disconnect,

};

//向 USB 核心注冊 struct usb_driver

static int __init usb_skel_init(void)

{

int result;

        result = usb_register(&skel_driver);

if (result)

                err("usb_register failed. Error number %d", result);

return result;

}

static void __exit usb_skel_exit(void)

{

        usb_deregister(&skel_driver);

}

探測和斷開的細節

在 struct usb_driver 結構中, 有 2 個 USB 核心在适當的時候調用的函數:

(1)當裝置安裝時, 如果 USB 核心認為這個驅動可以處理,則調用探測(probe)函數,探測函數檢查傳遞給它的裝置資訊, 并判斷驅動是否真正合适這個裝置.

(2)由于某些原因,裝置被移除或驅動不再控制裝置時,調用斷開(disconnect)函數,做适當清理.

探測和斷開回調函數都在 USB 集線器核心線程上下文中被調用, 是以它們休眠是合法的. 為了縮短 USB 探測時間,大部分工作盡可能在裝置打開時完成.這是因為 USB 核心是在一個線程中處理 USB 裝置的添加和移除, 是以任何慢裝置驅動都可能使 USB 裝置探測時間變長。

   探測函數分析 在探測回調函數中, USB 驅動應當初始化它可能用來管理 USB 裝置的所有本地結構并儲存所有需要的裝置資訊到本地結構, 因為在此時做這些通常更容易.為了和裝置通訊,USB 驅動通常要探測裝置的端點位址和緩沖大小. 以下是usb-skeleton的probe函數中的探測代碼:

    iface_desc = interface->cur_altsetting;//從輸入的interface中提取目前接口的端點總數

for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {

        endpoint = &iface_desc->endpoint[i].desc;//獲得端點的資料結構指針

if (!dev->bulk_in_endpointAddr &&

         usb_endpoint_is_bulk_in(endpoint)) {//如果是批量輸入端點,

            buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);

            dev->bulk_in_size = buffer_size;//獲得端點大小

            dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;//獲得端點位址

            dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);//為此端點建立緩沖區

if (!dev->bulk_in_buffer) {

                err("Could not allocate bulk_in_buffer");

goto error;

}

}

if (!dev->bulk_out_endpointAddr &&

         usb_endpoint_is_bulk_out(endpoint)) {    如果是批量輸出端點

            dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;//獲得端點位址

}

}

if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {//如果不是這兩種端點,報錯

        err("Could not find both bulk-in and bulk-out endpoints");

goto error;

}

//這裡端點判斷的函數給我們的程式設計帶來了友善:

static inline int usb_endpoint_num(const struct usb_endpoint_descriptor *epd)

{

return epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;

}

static inline int usb_endpoint_type(const struct usb_endpoint_descriptor *epd)

{

return epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;

}

static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)

{

return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);

}

static inline int usb_endpoint_dir_out(

const struct usb_endpoint_descriptor *epd)

{

return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);

}

static inline int usb_endpoint_xfer_bulk(

const struct usb_endpoint_descriptor *epd)

{

return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==

        USB_ENDPOINT_XFER_BULK);

}

static inline int usb_endpoint_xfer_control(

const struct usb_endpoint_descriptor *epd)

{

return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==

        USB_ENDPOINT_XFER_CONTROL);

}

static inline int usb_endpoint_xfer_int(

const struct usb_endpoint_descriptor *epd)

{

return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==

        USB_ENDPOINT_XFER_INT);

}

static inline int usb_endpoint_xfer_isoc(

const struct usb_endpoint_descriptor *epd)

{

return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==

        USB_ENDPOINT_XFER_ISOC);

}

static inline int usb_endpoint_is_bulk_in(

const struct usb_endpoint_descriptor *epd)

{

return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_in(epd));

}

static inline int usb_endpoint_is_bulk_out(

const struct usb_endpoint_descriptor *epd)

{

return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_out(epd));

}

static inline int usb_endpoint_is_int_in(

const struct usb_endpoint_descriptor *epd)

{

return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd));

}

static inline int usb_endpoint_is_int_out(

const struct usb_endpoint_descriptor *epd)

{

return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_out(epd));

}

static inline int usb_endpoint_is_isoc_in(

const struct usb_endpoint_descriptor *epd)

{

return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_in(epd));

}

static inline int usb_endpoint_is_isoc_out(

const struct usb_endpoint_descriptor *epd)

{

return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_out(epd));

}

在裝置注冊之後,USB 驅動的後續操作都是通過struct usb_interface 獲得裝置的端點資訊,是以要使用 usb_set_intfdata 将前面獲得的端點資訊儲存到struct usb_interface 下的struct device 中的void *driver_data;中,以友善以後的操作。在usb-skeleton的probe函數中的代碼:

    usb_set_intfdata(interface, dev);

之後在USB的驅動程式中的打開函數和斷開函數中調用usb_get_intfdata來擷取端點資料。由于這 2 個函數, USB 驅動不需要為系統中所有目前的裝置各保持一個靜态指針數組來儲存單個裝置結構. 對裝置資訊的非直接引用使得任何 USB 驅動都支援不限數量的裝置.

若這個 USB 驅動沒有和另一種處理使用者和裝置互動的子系統(如 input, tty, video......)關聯, 驅動可使用 USB 主裝置号,以便在使用者空間使用傳統的字元驅動接口. 為此, USB 驅動必須在探測函數中調用 usb_register_dev 函數, 以注冊一個裝置到 USB 核心. 在usb-skeleton的probe函數中的代碼:

    retval = usb_register_dev(interface, &skel_class);

if (retval) {

        err("Not able to get a minor for this device.");

        usb_set_intfdata(interface, NULL);

goto error;

}

//其中使用到的 struct usb_class_driver 結構體如下:

struct usb_class_driver {

char *name;//sysfs 用來描述裝置的名字

const struct file_operations *fops;// struct file_operations 結構指針, 驅動定義來注冊為字元裝置

int minor_base;

};

//而在usb-skeleton的設定如下:

static const struct file_operations skel_fops = {

.owner =    THIS_MODULE,

.read =        skel_read,

.write =    skel_write,

.open =        skel_open,

.release =    skel_release,

.flush =    skel_flush,

};

static struct usb_class_driver skel_class = {

.name ="skel%d",

.fops =&skel_fops,

.minor_base =    USB_SKEL_MINOR_BASE,

};

當 USB 裝置斷開, 所有關聯到這個裝置的資源都應被釋放,如果已在探測函數中調用 usb_register_dev 配置設定了 USB 裝置的次裝置号, 必須調用函數 usb_deregister_dev 來将次裝置号還回 USB 核心.在斷開函數中, 需要從接口擷取之前調用 usb_set_intfdata 所設定的資料,然後設定struct usb_interface 結構指針為 NULL,以防止錯誤的通路.而在usb-skeleton的源碼如下:

static void skel_disconnect(struct usb_interface *interface)

{

struct usb_skel *dev;

int minor = interface->minor;

    dev = usb_get_intfdata(interface);

    usb_set_intfdata(interface, NULL);

    usb_deregister_dev(interface, &skel_class);

    mutex_lock(&dev->io_mutex);

    dev->interface = NULL;

    mutex_unlock(&dev->io_mutex);

    usb_kill_anchored_urbs(&dev->submitted);

    kref_put(&dev->kref, skel_delete);

    info("USB Skeleton #%d now disconnected", minor);

}

當一個 USB 裝置調用 disconnect 函數時, 所有目前正被傳送的 urb 可自動被 USB 核心取消, 不必顯式調用 usb_kill_urb. 在USB裝置被斷開之後,如果驅動試圖調用 usb_submit_urb 送出urb , 将會失敗, 錯誤值為-EPIPE.

送出和控制 urb 的過程

以usb-skeleton源碼中的寫函數為例:

static ssize_t skel_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos)

{

struct usb_skel *dev;

int retval = 0;

struct urb *urb = NULL;

char *buf = NULL;

size_t writesize = min(count, (size_t)MAX_TRANSFER);

    dev = (struct usb_skel *)file->private_data;

if (count == 0)

goto exit;

if (down_interruptible(&dev->limit_sem)) {

        retval = -ERESTARTSYS;

goto exit;

}

    spin_lock_irq(&dev->err_lock);

if ((retval = dev->errors) < 0) {

        dev->errors = 0;

        retval = (retval == -EPIPE) ? retval : -EIO;

}

    spin_unlock_irq(&dev->err_lock);

if (retval < 0)

goto error;

    urb = usb_alloc_urb(0, GFP_KERNEL);

if (!urb) {

        retval = -ENOMEM;

goto error;

}

    buf = usb_buffer_alloc(dev->udev, writesize, GFP_KERNEL, &urb->transfer_dma);

if (!buf) {

        retval = -ENOMEM;

goto error;

}

if (copy_from_user(buf, user_buffer, writesize)) {

        retval = -EFAULT;

goto error;

}

    mutex_lock(&dev->io_mutex);

if (!dev->interface) {

        mutex_unlock(&dev->io_mutex);

        retval = -ENODEV;

goto error;

}

    usb_fill_bulk_urb(urb, dev->udev,

             usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),

             buf, writesize, skel_write_bulk_callback, dev);

    urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

    usb_anchor_urb(urb, &dev->submitted);

    retval = usb_submit_urb(urb, GFP_KERNEL);

    mutex_unlock(&dev->io_mutex);

if (retval) {

        err("%s - failed submitting write urb, error %d", __func__, retval);

goto error_unanchor;

}

    usb_free_urb(urb);

return writesize;

error_unanchor:

    usb_unanchor_urb(urb);

error:

if (urb) {

        usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);

        usb_free_urb(urb);

}

    up(&dev->limit_sem);

exit:

return retval;

}

//當urb被成功傳遞到 USB 裝置(或者在傳輸中發生了錯誤), urb 回調函數将被 USB 核心調用.也就是上面初始化 urb 中的 skel_write_bulk_callback

static void skel_write_bulk_callback(struct urb *urb)

{

struct usb_skel *dev;

    dev = urb->context;

if (urb->status) {

if(!(urb->status == -ENOENT ||

         urb->status == -ECONNRESET ||

         urb->status == -ESHUTDOWN))

            err("%s - nonzero write bulk status received: %d",

             __func__, urb->status);

        spin_lock(&dev->err_lock);

        dev->errors = urb->status;

        spin_unlock(&dev->err_lock);

}

    usb_buffer_free(urb->dev, urb->transfer_buffer_length,

            urb->transfer_buffer, urb->transfer_dma);

    up(&dev->limit_sem);

}

urb 回調函數是在中斷上下文運作, 是以它不應做任何記憶體配置設定, 持有任何信号量, 或任何可導緻程序休眠的事情. 如果從回調中送出 urb 并需要配置設定新記憶體塊, 需使用 GFP_ATOMIC 标志來告知 USB 核心不要休眠.

使用簡單的函數接口(urb函數的包裝)

有時隻是要發送或接受一些簡單的 USB 資料,可以使用簡單的函數接口:

int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,

         void *data, int len, int *actual_length, int timeout)

//struct usb_device *usb_dev :目标 USB 裝置指針

//unsigned int pipe :目标 USB 裝置的特定端點. 必須使用特定的宏建立.

//void *data :如果是 OUT 端點, 指向要發送到裝置的資料的指針. 如果是 IN 端點, 這是從裝置讀取的資料的緩沖區指針.

//int len : data 參數指向的緩沖的長度

//int *actual_length :指向函數放置真實位元組數的指針,根據端點方向,這些位元組要麼是被發送到裝置的,要麼是從裝置中讀取的.

//int timeout :時鐘嘀哒數, 應等待的時間. 如果為 0, 函數永遠等待操作完成.

//成功傳回0,actual_length 參數包含被傳送或從裝置中讀取的位元組數.否則傳回負的錯誤值.

int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,

         __u8 requesttype, __u16 value, __u16 index, void *data,

         __u16 size, int timeout)

//struct usb_device *usb_dev :目标 USB 裝置指針

//unsigned int pipe :目标 USB 裝置的特定端點. 必須使用特定的宏建立.

//__u8 request :控制消息的 USB 請求值.

//__u8 requesttype :控制消息的 USB 請求類型.

//__u16 value :控制消息的 USB 消息值.

//__u16 index :控制消息的 USB 消息索引值.

//void *data :如果是 OUT 端點, 指向要發送到裝置的資料的指針. 如果是 IN 端點, 這是從裝置讀取的資料的緩沖區指針.

//__u16 size : data 參數指向的緩沖的長度

//int timeout :時鐘嘀哒數, 應等待的時間. 如果為 0, 函數永遠等待操作完成.

//成功傳回被傳送到或從裝置讀取的位元組數.否則傳回負的錯誤值.

int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)

以上的函數不能在中斷上下文或持有自旋鎖時調用. 這些函數不能被取消, 是以小心使用; 確定驅動的 disconnect 函數了解足夠的資訊, 在允許它自己從記憶體被解除安裝之前等待調用結束.

 其他 USB 函數

USB 核心中的一些輔助函數用來從所有的 USB 裝置中擷取标準資訊. 這些函數不能在中斷上下文或者持有自旋鎖時調用,因為他們内部都是使用上面介紹的簡單的接口函數.這裡就不一一介紹了,包括《LDD3》介紹的這些函數,在/drivers/usb/core/message.c都有。

發表于: 2009-07-15,修改于: 2009-07-16 19:36,已浏覽1127次,有評論0條 推薦 投訴

網友評論

好文章,我是從你的第一章開始看的,今天9号看到你又有新内容,學不少東西,請堅持下去,加油啊

很不錯啊,我幾乎每天都來學習你的經驗,感覺收益非淺,加油啊!

我覺得最好的方式就是有人交流^_^我很想與你交流,我的QQ32313055

我學arm一年多了,可是水準比閣下差遠了。看完文章,收獲良多,非常感謝!

向Tekkaman  Ninja緻敬,你的學習态度和學習方式都值得我們借鑒!

聖誕快樂!讓我學到了很多東西,

很好的文章啊,我正在學習驅動開發,剛剛入門,謝謝啊

write error! code=-1 write ok! code=21 read ok! code=20 [0]=0 [1]=0 [2]=1 [3]=2 [4]=3[5]=4 [6]=5 [7]=6 [8]=7 [9]=8[10]=9 [11]=10 [12]=11 [13]=12 [14]=13[15]=14 [16]=15 [17]=16 [18]=17 [19]=18

我改了一下makefile在pc上運作是這個結果

所有的源碼都在友善之臂SBC2440V4上反複測試過(核心為2.6.22.2)。是以基本上隻要改Makefile就好(除了硬體相關的部分)。如果有問題可以将詳細的情況發郵件給我,有空我會回複的。

寫的真的很好我們老師就讓寫一個字元裝置驅動程式,我都是通過在你的網頁上學會的所有的東西。

贊一個,總結的很好曾經研究過ldd3,沒有認真寫下讀書筆記,以後還得好好再看下了

請問怎麼使用drivers/spi/atmel_spi.c提供的驅動呢?在/dev下沒有相應的裝置節點,怎麼才能在使用者空間通路裝置?如果自己寫一個驅動注冊一個spi驅動可以使用其中哪些函數接口?誠請幫忙

今天看了你的文章很感慨,希望能共同學習。有個問題想問,請問當insmod scull.ko scull_quantum=6 scull_qset=2資料已經溢出了,為什麼20個資料還能寫進去。謝謝了

這個和scull的資料結構有關,你認真看看《Linux裝置驅動程式學習(1)-字元裝置驅動程式》,中的圖,你可能對scull的資料結構還沒有了解透,scull_quantum代表了一個quantum的位元組數,scull_qset代表一個qset包含幾個quantum,但是還有qset沒有限制,也就是說我寫20個位元組進去,一個scull中有2個qset。

總結的太好了。有一個問題想請教一下。看子產品中定義了一個char *whom = "world"這是一個字元串常量,如果按照标準C的話,指針指向的是一個常量字元串才對,其中的内容不能修改。比如whom[2]='a';這樣會有段錯誤,為什麼在核心中這樣操作是合法的呢? 

我覺得你這樣是定義一個字元串變量,是以可以修改。我學硬體的,對c研究還不深,不知見解是否正确?

 lz的經驗和态度值得我們學習,向你緻敬!

很不錯,尤其是有源碼和測試程式。

你好!剛接觸linux驅動,看到你這裡的裝置模型分析,很不錯幫了我的大忙!我有一個問題,對于nand flash,作為一個塊裝置,應用程式如何去通路它,對它進行擦除、讀寫等操作?因為它不象字元裝置那樣有可供調用的系統調用

塊裝置我暫時還沒看,見諒

我也想知道塊裝置的一些知識,老是來你這兒看,希望能得到一些幫助,呵呵

塊裝置我還沒看啊,苦于沒時間和沒闆做實驗

問一個問題:你上文提到的測試程式是不是相當于linux系統的應用程式,實作對驅動的系統調用?如果是這樣,那麼測試程式應該放在什麼路徑下,用什麼編譯器進行編譯,因為我看到你有寫測試程式的makefile,是不是也要用交叉編譯器對它進行編譯阿,可是這樣的話,生成的目标代碼是arm格式的,不能在pc上直接執行。麻煩你幫我解答一下這個疑問。剛剛接觸linux,問得問題比較愚蠢,見笑了

測試程式是應用程式,在闆上運作

要交叉編譯

總結得很好   就像上完課了要複習  

很好的學習總結,受益匪淺!

[root@(none) scullc]# insmod scullc.koUsing scullc.konfs: server 222.31.45.9 not responding, still tryingnfs: server 222.31.45.9 not responding, still tryingnfs: server 222.31.45.9 not responding, still trying我按照傅大哥寫的子產品,一裝載的時候就出錯了........裡面的網絡沒問題,就是裝載了子產品後出問題的........傅大哥知道怎麼回事嗎???????

我是剛接觸linux的。今天看到你的總接非常高興,做的太詳細了。。。我想問一下,是否可以講解一下container_of是怎麼用的。。謝謝!

關于樓主描述的:最關鍵的“syslog(8,NULL,level)”語句我不了解,沒有找到相關資料。但是通過在ARM9闆上的實驗表明:程式是ok的!我用Hello world子產品做了實驗,現象和書上的一緻。我查閱了一下,應該這麼了解:原型是:int syslog(int type, char *bufp, int len);根據type來采取對核心ring buffer的操作,并設定consele_loglevel的值為len。type參數有以下幾種值:       傳回值的描述:       In case of error, -1 is returned, and errno is set. Otherwise,       for  type  equal  to 2, 3 or 4, syslog() returns the number of       bytes read, and otherwise 0.不妥之處請指教,謝謝。

文章寫的很好,剛開始學驅動,有很多地方都不是很懂,很想跟你學習啊,想請教一下學習linux裝置驅動的方法。

問一個很弱的問題,樓主的源碼檔案是如何存儲在cublog裡面的,敬請告知,不勝感激

write code=6write code=6write code=6write code=2read code=6read code=6read code=6read code=2[0]=0 [1]=1 [2]=2 [3]=3 [4]=4[5]=5 [6]=6 [7]=7 [8]=8 [9]=9[10]=10 [11]=11 [12]=12 [13]=13 [14]=14[15]=15 [16]=16 [17]=17 [18]=18 [19]=19 看你的測試程式中 輸出語句 要麼是 work error! code=..   或者 work ok! code=..    上面的代碼輸出 work  code=...   既沒有error! 也沒有ok! 什麼原因啊?在字元驅動那張圖 scull_device所指的連結清單中 Scull_qset 這種結構的個數如何确定呢。insmod scull_debug.ko scull_nr_devs=1 scull_quantum=6 scull_qset=2後我測試的結果是:write error! code=6write error! code=6write error! code=6write ok! code=2read error! code=6read error! code=6read error! code=6read ok! code=2[0]=0 [1]=1 [2]=2 [3]=3 [4]=4[5]=5 [6]=6 [7]=7 [8]=8 [9]=9[10]=10 [11]=11 [12]=12 [13]=13 [14]=14[15]=15 [16]=16 [17]=17 [18]=18 [19]=19請問如何解釋啊。

從樓主這裡學到不少東西void kobject_del(struct kobject *kobj); 寫錯了

編譯的時候出現[[email protected] scull]# make modulesmake -C /home/alu/mywork/systems/linux-2.6.22.6  M=/home/soso/ldd3/scull   modulesmake[1]: Entering directory `/home/alu/mywork/systems/linux-2.6.22.6'  CC [M]  /home/soso/ldd3/scull/scull_t1.occ1: invalid option `little-endian'cc1: invalid option `arm'cc1: invalid option `apcs'cc1: invalid option `no-sched-prolog'cc1: invalid option `apcs-32'cc1: invalid option `no-thumb-interwork'cc1: invalid option `tune=arm9tdmi'cc1: invalid option `alignment-traps'cc1: unrecognized option `-Wdeclaration-after-statement'make[2]: *** [/home/soso/ldd3/scull/scull_t1.o] 錯誤 1make[1]: *** [_module_/home/soso/ldd3/scull] 錯誤 2make[1]: Leaving directory `/home/alu/mywork/systems/linux-2.6.22.6'make: *** [modules] 錯誤 2請教大哥  這個大概可能是什麼問題啊  那麼多參數無效  核心版本2.6.22.6 arm-linux-gcc -vgcc version 3.3.6

"include/linux/moduleparam.h檔案中有關于module_param_array宏的說明,這裡第三個參數的作用是:*nump is set to number they actually specified是以這裡的TNparam_nr并不是限制輸入數組參數的數目,而是實際資料的大小。可以在上面的示例代碼中加一句:printk(KERN_ALERT "TNparam_nr=%d/n",TNparam_nr);得到的結果會是你實際輸入的數組元素個數。BTW:筆記寫得這麼詳細,我都懶得寫了,直接收藏你的筆記了:-)

感謝兄台指點!!!!!!!

新學驅動開發   跟着ldd3看不懂  來支援下你   

        while (n--) {        if (!qs->next) {            qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);            if (qs->next == NULL)                return NULL;              memset(qs->next, 0, sizeof(struct scull_qset));        }        qs = qs->next;        continue;    }    return qs;}continue;有什麼作用

正如注釋所說的: 是不斷的跟随雙向連結清單到底!

 網友: 本站網友  時間:2009-04-12 09:43:54 IP位址:159.226.139.★            while (n--) {        if (!qs->next) {            qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);            if (qs->next == NULL)                return NULL;              memset(qs->next, 0, sizeof(struct scull_qset));        }        qs = qs->next;        continue;    }    return qs;}continue;有什麼作用Blog作者的回複:正如注釋所說的: 是不斷的跟随雙向連結清單到底! 去掉continue;依然可以繼續循環,continue是多餘的???  

 Tekkaman:            你好,首先向你的工作緻敬,我很希望和你一起探讨核心題,對于 文中提到“我這個實驗除了對參數的改變進行實驗外,我的一個重要的目的是測試“ module_param_array(TNparam , int , &TNparam_nr , S_IRUGO);”中&TNparam_nr對輸入參數數目的限制作用。經過我的實驗,表明&TNparam_nr并沒有對輸入參數的數目起到限制作用。真正起到限制作用的是“static int TNparam[] = {1,2,3,4};”本身定義的大小,我将程式進行修改:”  我作了測試,當insmod hello-param.ko howmany=2 whom="KeKe" TNparam=4,3,2,1,5,時,系統報錯“insmod:error inserting 'paramadd.ko":-1 Invalid parameters說明&TNparam_nr有對輸入參數的數目起到限制作用,我用的是Fedora8

部落客你是我見過的最好的部落客了!永遠支援你!

man syslog  如下:NAME       syslog,  klogctl  -  read  and/or clear kernel message ring buffer; set       console_loglevelSYNOPSIS       int syslog(int type, char *bufp, int len);                                     #include <sys/klog.h>       int klogctl(int type, char *bufp, int len);DESCRIPTION       If you need the libc function syslog()  (which  talks  to  syslogd(8)),       then look at syslog(3).  The system call of this name is about control‐       ling the kernel printk()  buffer,  and  the  glibc  version  is  called       klogctl().       The type argument determines the action taken by this function.       Quoting from kernel/printk.c:              Only  command types 3 and 10 are allowed to non-root processes.  Type 9       was added in 2.4.10; type 10 in 2.6.6.   The kernel log buffer       The kernel has a cyclic buffer of length LOG_BUF_LEN in which  messages       given  as arguments to the kernel function printk() are stored (regard‐       less of their loglevel).  In early kernels, LOG_BUF_LEN had  the  value       4096;  from  kernel  1.3.54,  it  was  8192; from kernel 2.1.113 it was       16384; since 2.4.23/2.6 the value is a kernel configuration option.  In       recent kernels the size can be queried with command type 10.       The  call  syslog(2,buf,len)  waits  until  this  kernel  log buffer is       nonempty, and then reads at most len bytes into  the  buffer  buf.   It       returns  the  number  of bytes read.  Bytes read from the log disappear       from the log buffer: the information can only be read  once.   This  is       the  function  executed  by  the  kernel  when  a  user  program  reads       /proc/kmsg.       The call syslog(3,buf,len) will read the last len bytes  from  the  log       buffer  (non-destructively),  but  will  not read more than was written       into the buffer since the last "clear ring buffer" command (which  does       not clear the buffer at all).  It returns the number of bytes read.       The  call  syslog(4,buf,len) does precisely the same, but also executes       the "clear ring buffer" command.       The call syslog(5,dummy,dummy) executes just the  "clear  ring  buffer"       command.  (In each call where buf or len is shown as "dummy", the value       of the argument is ignored by the call.)       The call syslog(6,dummy,dummy) sets the console log level  to  minimum,       so that no messages are printed to the console.       The  call  syslog(7,dummy,dummy) sets the console log level to default,       so that messages are printed to the console.       The call syslog(8,dummy,level) sets the console  log  level  to  level,       which must be an integer between 1 and 8 (inclusive).  See the loglevel       section for details.       The call syslog(9,dummy,dummy) returns the number  of  bytes  currently       available to be read on the kernel log buffer.       The  call  syslog(10,dummy,dummy)  returns the total size of the kernel       log buffer.   The loglevel       The kernel routine printk() will only print a message on  the  console,       if  it  has  a  loglevel  less  than  the  value  of  the variable con‐       sole_loglevel.  This variable  initially  has  the  value  DEFAULT_CON‐       SOLE_LOGLEVEL (7), but is set to 10 if the kernel command line contains       the word "debug", and to 15 in case of a kernel fault (the  10  and  15       are just silly, and equivalent to 8).  This variable is set (to a value       in the range 1-8) by the call syslog(8,dummy,value).   The  calls  sys‐       log(type,dummy,dummy)  with  type  equal to 6 or 7, set it to 1 (kernel       panics only) or 7 (all except debugging messages), respectively.       Every text line in a message has  its  own  loglevel.   This  level  is       DEFAULT_MESSAGE_LOGLEVEL  - 1 (6) unless the line starts with <d> where       d is a digit in the range 1-7, in which case the level is d.  The  con‐       ventional  meaning  of  the  loglevel is defined in <linux/kernel.h> as       follows:       #define KERN_EMERG    "<0>"         #define KERN_ALERT    "<1>"         #define KERN_CRIT     "<2>"         #define KERN_ERR      "<3>"         #define KERN_WARNING  "<4>"         #define KERN_NOTICE   "<5>"         #define KERN_INFO     "<6>"         #define KERN_DEBUG    "<7>"  RETURN VALUE       For type equal to 2, 3, or 4, a successful call to syslog() returns the       number of bytes read.  For type 9, syslog() returns the number of bytes       currently available to be read on the kernel log buffer.  For type  10,       syslog() returns the total size  of  the  kernel log buffer.  For other       values of type, 0 is returned on success.       In case of error, -1 is returned, and errno  is  set  to  indicate  the       error.ERRORS       EINVAL Bad  parameters  (e.g., bad type; or for type 2, 3, or 4, buf is              NULL, or len is less than zero; or for type 8, the level is out‐              side the range 1 to 8).       EPERM  An attempt was made to change console_loglevel or clear the ker‐              nel message ring buffer by a process  without  root  permissions              (more precisely: without the CAP_SYS_ADMIN capability).       ERESTARTSYS              System  call  was  interrupted  by  a  signal; nothing was read.              (This can be seen only during a trace.)CONFORMING TO       This system call is Linux-specific and should not be used  in  programs       intended to be portable.NOTES       From  the  very start people noted that it is unfortunate that a system       call and a library routine of the same name are entirely different ani‐       mals.   In  libc4  and  libc5  the  number  of this call was defined by       SYS_klog.  In glibc 2.0 the syscall is baptized klogctl().SEE ALSO       syslog(3)COLOPHON       This page is part of release 2.79 of the Linux  man-pages  project.   A       description  of  the project, and information about reporting bugs, can       be found at http://www.kernel.org/doc/man-pages/.

您好,我是為向您求教才注冊的。我現在想以動态配置設定裝置号的方式加載驅動,并建立裝置節點。由于是新手,是以好多地方不是很明白。總之,我希望能在驅動加載上就自動建立裝置檔案節點,方法1:在網上看到好像說是在驅動中用class之類的可以達到這個效果;方法2:希望通過讀取/proc/dev得到的消息來建立裝置檔案節點,但沒寫過shell腳本,且不知道怎樣讓它自動運作。希望得到您的提示。謝謝

要自動建立節點的話必須實作:

1、檔案系統中mdev或udev

2、驅動中實作class(或是用偷懶點的辦法:直接注冊misc類)。

剛才申請的帳号居然登入不上,是以隻能匿名了,郁悶……

部落客,請問一下你的hello子產品沒什麼沒寫Kconfig檔案,我看有的書上說添加子產品必須寫kconfig檔案啊····?請指教,謝謝!

我是在核心外編譯子產品,不用Kconfig

請問樓主你的程式哪裡來的,書上都不全呀?

我的程式是根據《LDD3》的程式修改、增删而成的。 

請問測試程式read(sculltest , &buffer2[20-i] , i),write(sculltest , &buffer1[20-i] , i)兩個函數與驅動是怎麼挂鈎,是怎麼樣一個運作機制?謝謝。

哎……驅動層與使用者層的,代碼上的聯系是怎麼樣的?說實話,這個驅動我看完了都不知道它能做什麼?更不知道測試程式應該怎麼寫...慚愧啊

是以在學驅動前一定要學一點應用層的東西,寫一些應用層的小軟體試試,不如驅動是學不下去的。

請問你當初在應用層是如何學習的?謝謝指點。

我看到你也是硬體出身,很期待你對于應用層開發入門的建議。

我是初學者,我按照你提供的程式輸入make modules 輸出提示:missing separator. Stop.

不管讀,還是寫,都休眠,然後就卡在那了,動也動不了,大概是什麼原因

部落客好久沒發 新的關于驅動的啦 ,終于來啦 哈哈,激動

部落客:      我是初學者,寫2440的linux驅動時會用到一些例如s3c2410_gpio_setpin()的函數,這些函數的定義在核心的哪個目錄下呢?有沒有一些系統的函數用法說明啊?

部落客:請教個問題,你在驅動程式IO_irq.c中的函數為int IO_irq_ioctl(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg)而你在應用程式IO_irq_test.c中調用的if ((code=ioctl( IO_irqtest , IO_IRQ_1  , NULL ) ) < 0) ,沒有參數struct inode *inode,能解釋下嗎?謝謝

哈哈,最近也在學ldd3,每學完一章就來你這兒看一下你的筆記,實在是寫得太好了。

我剛學習linux驅動,請問以下問題怎麼解決呢?[[email protected] LinuxDriver]# insmod ./hello.o./hello.o: kernel-module version mismatch        ./hello.o was compiled for kernel version 2.4.20        while this kernel is version 2.4.20-8.

你編譯子產品使用的核心版本和運作子產品的核心版本不同,建議統一下。

或者,好像在核心配置上可以關閉這個檢查。

喜歡注釋  感謝一個。。。。

牛人,兄弟你太有才了,前途無量!

總結的不錯....轉了....謝謝!

拜讀美文,醍醐灌頂,如夢初醒。

繼續閱讀