linux : firmware 簡介
我以為device要可以運作, 只要在linux安裝相關的driver即可
最近看到原來device除了driver之外, 還是需要firmware這東西才可以運作...
稍微做個筆記....
Firmware
基本上device要可以正常運作是需要driver和firmware.driver可以看成是系統跟device溝通的橋樑. firmware則是執行在device上面的程式.
Firmware除了可以預先寫死在device上外,另一方式就是driver初始化device的時候. 從系統讀取firmware, 在寫入到device.
Firmware讀取流程
linux放置firmware的地方為/lib/firmware. 讀取firmware的時候就是透過此.
(1) 流程參考[2]
- Driver跟kernel要求 "ar9170.fw"
- kernel傳遞event給udev來要求firmware
- udev執行對應的script,把firmware傳送至kernel所創建的一個特殊檔案
- kernel從此特殊檔案讀取firmware, 再把資料傳送給driver
- driver再把資料傳給device
1), kernel(driver): - calls request_firmware(&fw_entry, $FIRMWARE, device) - kernel searchs the fimware image with name $FIRMWARE directly in the below search path of root filesystem: User customized search path by module parameter 'path'[1] "/lib/firmware/updates/" UTS_RELEASE, "/lib/firmware/updates", "/lib/firmware/" UTS_RELEASE, "/lib/firmware" - If found, goto 7), else goto 2) [1], the 'path' is a string parameter which length should be less than 256, user should pass 'firmware_class.path=$CUSTOMIZED_PATH' if firmware_class is built in kernel(the general situation) 2), userspace: - /sys/class/firmware/xxx/{loading,data} appear. - hotplug gets called with a firmware identifier in $FIRMWARE and the usual hotplug environment. - hotplug: echo 1 > /sys/class/firmware/xxx/loading 3), kernel: Discard any previous partial load. 4), userspace: - hotplug: cat appropriate_firmware_image > \ /sys/class/firmware/xxx/data 5), kernel: grows a buffer in PAGE_SIZE increments to hold the image as it comes in. 6), userspace: - hotplug: echo 0 > /sys/class/firmware/xxx/loading 7), kernel: request_firmware() returns and the driver has the firmware image in fw_entry->{data,size}. If something went wrong request_firmware() returns non-zero and fw_entry is set to NULL. 8), kernel(driver): Driver code calls release_firmware(fw_entry) releasing the firmware image and any related resource.
Trace driver Code
這邊trace位於drivers/net/ethernet/brocade/bna底下的兩個檔案bfa_ioc.c和bnad_ethtool.c.
bnad_ethtool.c這邊可以看到driver透過request_firmware函數去跟kernel要求firmware.
在寫入至device之後就透過release_firmware把firmware給釋放掉了.
而bfa_nw_flash_update_part和bfa_flash_write_send則是driver如何把firmware寫入至device的流程.
static int bnad_flash_device(struct net_device *netdev, struct ethtool_flash *eflash) { struct bnad *bnad = netdev_priv(netdev); struct bnad_iocmd_comp fcomp; const struct firmware *fw; int ret = 0; ret = request_firmware(&fw, eflash->data, &bnad->pcidev->dev); if (ret) { netdev_err(netdev, "can't load firmware %s\n", eflash->data); goto out; } fcomp.bnad = bnad; fcomp.comp_status = 0; init_completion(&fcomp.comp); spin_lock_irq(&bnad->bna_lock); ret = bfa_nw_flash_update_part(&bnad->bna.flash, BFA_FLASH_PART_FWIMG, bnad->id, (u8 *)fw->data, fw->size, 0, bnad_cb_completion, &fcomp); if (ret != BFA_STATUS_OK) { netdev_warn(netdev, "flash update failed with err=%d\n", ret); ret = -EIO; spin_unlock_irq(&bnad->bna_lock); goto out; } spin_unlock_irq(&bnad->bna_lock); wait_for_completion(&fcomp.comp); if (fcomp.comp_status != BFA_STATUS_OK) { ret = -EIO; netdev_warn(netdev, "firmware image update failed with err=%d\n", fcomp.comp_status); } out: release_firmware(fw); return ret; } /** * bfa_nw_flash_update_part - Update flash partition. * * @flash: flash structure * @type: flash partition type * @instance: flash partition instance * @buf: update data buffer * @len: data buffer length * @offset: offset relative to the partition starting address * @cbfn: callback function * @cbarg: callback argument * * Return status. */ enum bfa_status bfa_nw_flash_update_part(struct bfa_flash *flash, u32 type, u8 instance, void *buf, u32 len, u32 offset, bfa_cb_flash cbfn, void *cbarg) { if (!bfa_nw_ioc_is_operational(flash->ioc)) return BFA_STATUS_IOC_NON_OP; /* * 'len' must be in word (4-byte) boundary */ if (!len || (len & 0x03)) return BFA_STATUS_FLASH_BAD_LEN; if (type == BFA_FLASH_PART_MFG) return BFA_STATUS_EINVAL; if (flash->op_busy) return BFA_STATUS_DEVBUSY; flash->op_busy = 1; flash->cbfn = cbfn; flash->cbarg = cbarg; flash->type = type; flash->instance = instance; flash->residue = len; flash->offset = 0; flash->addr_off = offset; flash->ubuf = buf; bfa_flash_write_send(flash); return BFA_STATUS_OK; } /* * Send flash write request. */ static void bfa_flash_write_send(struct bfa_flash *flash) { struct bfi_flash_write_req *msg = (struct bfi_flash_write_req *) flash->mb.msg; u32 len; msg->type = be32_to_cpu(flash->type); msg->instance = flash->instance; msg->offset = be32_to_cpu(flash->addr_off + flash->offset); len = (flash->residue < BFA_FLASH_DMA_BUF_SZ) ? flash->residue : BFA_FLASH_DMA_BUF_SZ; msg->length = be32_to_cpu(len); /* indicate if it's the last msg of the whole write operation */ msg->last = (len == flash->residue) ? 1 : 0; bfi_h2i_set(msg->mh, BFI_MC_FLASH, BFI_FLASH_H2I_WRITE_REQ, bfa_ioc_portid(flash->ioc)); bfa_alen_set(&msg->alen, len, flash->dbuf_pa); memcpy(flash->dbuf_kva, flash->ubuf + flash->offset, len); bfa_nw_ioc_mbox_queue(flash->ioc, &flash->mb, NULL, NULL); flash->residue -= len; flash->offset += len; }
Reference
- Linux Tv: Firmware
- Ubuntu: Firmware
- Linux 内核 firmware 加载过程
- 14.8. Dealing with Firmware
- linux-x.y.z/Documentation/firmware_class/README
留言
張貼留言