前文提到ufs-utils这个工具可以做UFS FFU,我们先看这个工具的使用。

ufs-util

Android自带的aarch64貌似编译有问题,换个工具编译ok,ffu怎么用了?

./ufs-utils ffu --help

 FFU command usage:

        ufs-utils ffu [-t] <ffu cmd idn> [-p] <path to device>?

        -t       FFU cmd idn
                         0  : FFU, flash FFU
                         1  : Check FFU status (check FFU status attribute and display FW version)

        -s       Max chunk size in KB alignment to 4KB, which FFU file will be split (optional)

        -w       path to FFU file

        -g       sg struct ver - 0: SG_IO_VER4 (default), 1: SG_IO_VER3

        -p       bsg device path for FFU, ufs-bsg for Check FFU status

-p的bsg device path就是前面提到的/dev/0:0:0:0(sg v4) or /dev/block/sda(sg v3)。

usf-bsg用于Check FFU status,device是/dev/ufs-bsg。

what is ufs bsg? 看文档:

  1. BSG Support

This transport driver supports exchanging UFS protocol information units
(UPIUs) with a UFS device. Typically, user space will allocate
struct ufs_bsg_request and struct ufs_bsg_reply (see ufs_bsg.h) as
request_upiu and reply_upiu respectively. Filling those UPIUs should
be done in accordance with JEDEC spec UFS2.1 paragraph 10.7.
Caveat emptor: The driver makes no further input validations and sends the
UPIU to the device as it is. Open the bsg device in /dev/ufs-bsg and
send SG_IO with the applicable sg_io_v4::

   io_hdr_v4.guard = 'Q';
   io_hdr_v4.protocol = BSG_PROTOCOL_SCSI;
   io_hdr_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT;
   io_hdr_v4.response = (__u64)reply_upiu;
   io_hdr_v4.max_response_len = reply_len;
   io_hdr_v4.request_len = request_len;
   io_hdr_v4.request = (__u64)request_upiu;
   if (dir == SG_DXFER_TO_DEV) {
           io_hdr_v4.dout_xfer_len = (uint32_t)byte_cnt;
           io_hdr_v4.dout_xferp = (uintptr_t)(__u64)buff;
   } else {
           io_hdr_v4.din_xfer_len = (uint32_t)byte_cnt;
           io_hdr_v4.din_xferp = (uintptr_t)(__u64)buff;
   }
config SCSI_UFS_BSG
        bool "Universal Flash Storage BSG device node"
        depends on SCSI_UFSHCD
        select BLK_DEV_BSGLIB
        help
          Universal Flash Storage (UFS) is SCSI transport specification for
          accessing flash storage on digital cameras, mobile phones and
          consumer electronic devices.
          A UFS controller communicates with a UFS device by exchanging
          UFS Protocol Information Units (UPIUs).
          UPIUs can not only be used as a transport layer for the SCSI protocol
          but are also used by the UFS native command set.
          This transport driver supports exchanging UFS protocol information units
          with a UFS device. See also the ufshcd driver, which is a SCSI driver
          that supports UFS devices.

          Select this if you need a bsg device node for your UFS controller.
          If unsure, say N.

重点是UPIUs are also used by the UFS native command set

ufshcd-core-$(CONFIG_SCSI_UFS_BSG)   += ufs_bsg.o

code是driver\scsi\ufs\ufs_bsg.c

/*
 * bsg endpoint that supports UPIUs
 *
 * Copyright (C) 2018 Western Digital Corporation
 */
#include "ufs_bsg.h"

当前QCOM低内核还不支持ufs bsg,我从5.x port过来可以用了,有些接口小冲突需要fix。

不过drv有对应属性是:

UFS_ATTRIBUTE(ffu_status, _FFU_STATUS);

sysfs在:

What: /sys/bus/platform/drivers/ufshcd/*/attributes/ffu_status
Date: February 2018
Contact: Stanislav Nijnikov <stanislav.nijnikov@wdc.com>
Description: This file provides the ffu status UFS device attribute.

            The full information about the attribute could be found at
            UFS specifications 2.1.
            The file is read only.

ufs spec的定义:

ffu-status.png

The column "MDV" (Manufacturer Default Value) specifies atrribute values after device manufacturing

就是出厂值是0了,升级成功是1。

ok,QCOM平台直接使用如下命令升级(固件升级文件是xxx.bin):

./ufs-utils ffu -t 0 -w xxx.bin -p /dev/block/sda

nm,报错了,一下心哇凉哇凉的:

Start : write_buffer mode 14 , buf_id 0
Start : send_scsi_cmd cmd = 3b len 262144 sg_type 0

writing file scsi_cmd_cdb_1.bin length=10


 Err: Command fail with status 1 , senseKey Miscompare

 Err: SG_IO WRITE BUFFER data error ret -22

 Err: Write error -22:

工具默认没开debug,打开即可:

-#CXXFLAGS = -DDEBUG
+CXXFLAGS = -DDEBUG

senseKey Miscompare是啥意思?我们先看工具code:

/* description of the sense key values */
static const char *const snstext[] = {
        "No Sense",         /* 0: There is no sense information */
        "Recovered Error",  /* 1: The last command completed successfully
                                  but used error correction */
        "Not Ready",        /* 2: The addressed target is not ready */
        "Medium Error",     /* 3: Data error detected on the medium */
        "Hardware Error",   /* 4: Controller or device failure */
        "Illegal Request",  /* 5: Error in request */
        "Unit Attention",   /* 6: Removable medium was changed, or
                                  the target has been reset, or ... */
        "Data Protect",     /* 7: Access to the data is blocked */
        "Blank Check",      /* 8: Reached unexpected written or unwritten
                                  region of the medium */
        "Vendor Specific",
        "Copy Aborted",     /* A: COPY or COMPARE was aborted */
        "Aborted Command",  /* B: The target aborted the command */
        "Equal",            /* C: A SEARCH DATA command found data equal */
        "Volume Overflow",  /* D: Medium full with still data to be written */
        "Miscompare",       /* E: Source data and data on the medium
                                  do not agree */
/**
 * send_scsi_cmd - Utility function for SCSI command sending
 * @fd: bsg driver file descriptor
 * @cdb: pointer to SCSI cmd cdb buffer
 * @buf: pointer to the SCSI cmd data buffer
 * @cmd_len: SCSI command length
 * @byte_cnt: SCSI data length
 * @dir: The cmd direction
 *
 **/
static int send_scsi_cmd(int fd, const __u8 *cdb, void *buf, __u8 cmd_len,
                __u32 byte_cnt, int dir, __u8 sg_type)
{
    ...
        else {
                io_hdr_v3.interface_id = 'S';
                io_hdr_v3.cmd_len = cmd_len;
                io_hdr_v3.mx_sb_len = SENSE_BUFF_LEN;
                io_hdr_v3.dxfer_direction = dir;
                io_hdr_v3.dxfer_len = byte_cnt;
                io_hdr_v3.dxferp = buf;
                /* pointer to command buf (rbufCmdBlk) */
                io_hdr_v3.cmdp = (unsigned char *)cdb;
                io_hdr_v3.sbp = sense_buffer; //tj: here
                io_hdr_v3.timeout = DEF_TIMEOUT_MSEC;
                sg_struct = &io_hdr_v3;
        }
        WRITE_LOG("Start : %s cmd = %x len %d sg_type %d\n", __func__, cdb[0],
                        byte_cnt, sg_type);

        write_file_with_counter("scsi_cmd_cdb_%d.bin",
                        cdb, cmd_len);

        while (((ret = ioctl(fd, SG_IO, sg_struct)) < 0) &&
                ((errno == EINTR) || (errno == EAGAIN)));
        if (sg_type == SG4_TYPE) {
                if (io_hdr_v4.info != 0) {
                        print_error("Command fail with status %x , senseKey %s",
                                io_hdr_v4.info,
                                sense_key_string(sense_buffer[2]));
                        ret = -EINVAL;
                }
        }
        else {
                if (io_hdr_v3.status) {
                        print_error("Command fail with status %x , senseKey %s",
                                io_hdr_v3.status,
                                sense_key_string(sense_buffer[2]));
                        ret = -EINVAL;
                }

        }

用的是v3,io_hdr_v3.status是1,ioctl cmd是SG_IO

看下v3 interface:

/*
 * SCSI Generic v3 struct copied from include/scsi/sg.h
 */
typedef struct sg_io_hdr {
        int interface_id;           /* [i] 'S' for SCSI generic (required) */
        int dxfer_direction;        /* [i] data transfer direction  */
        unsigned char cmd_len;      /* [i] SCSI command length ( <= 16 bytes) */
        unsigned char mx_sb_len;    /* [i] max length to write to sbp */
        unsigned short int iovec_count; /* [i] 0 implies no scatter gather */
        unsigned int dxfer_len;     /* [i] byte count of data transfer */
        void *dxferp;              /* [i], [*io] points to data transfer memory
                                 or scatter gather list */
        unsigned char *cmdp;       /* [i], [*i] points to command to perform */
        unsigned char *sbp;        /* [i], [*o] points to sense_buffer memory */ //tj:here

sbp就是响应了,看样子是写进去了,怎么会不匹配,拍了拍脑袋想了想改了改代码居然可以了,容我慢慢道来:)

FFU was written to the device, reboot and check status
剩余内容付款5.00元后3天内可查看