前文提到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