第 4 章 - USBX Pictbridge 实现

USBX 在主机和设备上都支持完全 Pictbridge 实现。 在两端,Pictbridge 都位于 USBX PIMA 类之上。

PictBridge 标准允许将数码照相机或智能手机直接连接到打印机,而不使用 PC,从而可以直接使用特定的 Pictbridge 感知打印机进行打印。

当照相机或手机连接到打印机时,打印机即为 USB 主机,照相机即为 USB 设备。 然而,在使用 Pictbridge 时,相机显示为主机,而且命令是从相机驱动的。 相机是存储服务器,打印机是存储客户端。 相机是打印客户端,打印机当然是打印服务器。

Pictbridge 使用 USB 作为传输层,但依赖于 PTP(图片传输协议)作为通信协议。

下图展示了在执行打印作业时 DPS 客户端与 DPS 服务器之间的命令/响应:

DPS commands and responses

Pictbridge 客户端实现

客户端上的 Pictbridge 要求先运行 USBX 设备堆栈和 PIMA 类。

设备框架以如下方式描述 PIMA 类。

UCHAR device_framework_full_speed[] =
{
    /* Device descriptor */
    0x12, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, 0x20,
    0xA9, 0x04, 0xB6, 0x30, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x01,
    /* Configuration descriptor */
    0x09, 0x02, 0x27, 0x00, 0x01, 0x01, 0x00, 0xc0, 0x32,
    /* Interface descriptor */
    0x09, 0x04, 0x00, 0x00, 0x03, 0x06, 0x01, 0x01, 0x00,
    /* Endpoint descriptor (Bulk Out) */
    0x07, 0x05, 0x01, 0x02, 0x40, 0x00, 0x00,
    /* Endpoint descriptor (Bulk In) */
    0x07, 0x05, 0x82, 0x02, 0x40, 0x00, 0x00,
    /* Endpoint descriptor (Interrupt) */
    0x07, 0x05, 0x83, 0x03, 0x08, 0x00, 0x60
};

PIMA 类使用 ID 字段 0x06,对于静态图像,它的子类为 0x01,对于 PIMA 15740,它的协议为 0x01。

此类中定义了 3 个终结点,2 个用于发送/接收数据的批处理,1 个用于事件的中断。

与其他 USBX 设备实现不同,Pictbridge 应用程序本身不需要定义类, 而是调用函数 ux_pictbridge_dpsclient_start。 下面是一个示例。

/* Initialize the Pictbridge string components. */
ux_utility_memory_copy
    (pictbridge.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_vendor_name,
    "ExpressLogic",13);

ux_utility_memory_copy
    (pictbridge.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_product_name,
    "EL_Pictbridge_Camera",21);

ux_utility_memory_copy
    (pictbridge.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_serial_no, "ABC_123",7);

ux_utility_memory_copy
    (pictbridge.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_dpsversions,
    "1.0 1.1",7);

pictbridge.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_vendor_specific_version = 0x0100;

/* Start the Pictbridge client. */
status = ux_pictbridge_dpsclient_start(&pictbridge);

if(status != UX_SUCCESS)
    return;

传递给 pictbridge 客户端的参数如下所示。

pictbridge.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_vendor_name
    : String of Vendor name
pictbridge.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_product_name
    : String of product name
pictbridge.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_serial_no
    : String of serial number
pictbridge.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_dpsversions
    : String of version
pictbridge.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_vendor_specific_version
    : Value set to 0x0100;

下一步是让设备和主机同步,并准备好进行信息交换。

这是通过等待事件标志来完成的,如下所示。

/* We should wait for the host and the client to discover one another. */
status = ux_utility_event_flags_get(&pictbridge.ux_pictbridge_event_flags_group,
    UX_PICTBRIDGE_EVENT_FLAG_DISCOVERY,TX_AND_CLEAR,
    &actual_flags, UX_PICTBRIDGE_EVENT_TIMEOUT);

如果状态机处于“DISCOVERY_COMPLETE”状态,则照相机端(DPS 客户端)将收集有关打印机及其功能的信息。

如果 DPS 客户端已准备好接受打印作业,则其状态设置为“UX_PICTBRIDGE_NEW_JOB_TRUE”。 可以按如下进行检查。

/* Check if the printer is ready for a print job. */
if (pictbridge.ux_pictbridge_dpsclient.ux_pictbridge_devinfo_newjobok ==
    UX_PICTBRIDGE_NEW_JOB_TRUE)
/* We can print something … */

接下来,需要按如下方式填充一些打印工作描述符:

/* We can start a new job. Fill in the JobConfig and PrintInfo structures. */
jobinfo = &pictbridge.ux_pictbridge_jobinfo;

/* Attach a printinfo structure to the job. */
jobinfo -> ux_pictbridge_jobinfo_printinfo_start = &printinfo;

/* Set the default values for print job. */
jobinfo -> ux_pictbridge_jobinfo_quality =
    UX_PICTBRIDGE_QUALITIES_DEFAULT;
jobinfo -> ux_pictbridge_jobinfo_papersize =
    UX_PICTBRIDGE_PAPER_SIZES_DEFAULT;
jobinfo -> ux_pictbridge_jobinfo_papertype =
    UX_PICTBRIDGE_PAPER_TYPES_DEFAULT;
jobinfo -> ux_pictbridge_jobinfo_filetype =
    UX_PICTBRIDGE_FILE_TYPES_DEFAULT;
jobinfo -> ux_pictbridge_jobinfo_dateprint =
    UX_PICTBRIDGE_DATE_PRINTS_DEFAULT;
jobinfo -> ux_pictbridge_jobinfo_filenameprint =
    UX_PICTBRIDGE_FILE_NAME_PRINTS_DEFAULT;
jobinfo -> ux_pictbridge_jobinfo_imageoptimize =
    UX_PICTBRIDGE_IMAGE_OPTIMIZES_OFF;
jobinfo -> ux_pictbridge_jobinfo_layout =
    UX_PICTBRIDGE_LAYOUTS_DEFAULT;
jobinfo -> ux_pictbridge_jobinfo_fixedsize =
    UX_PICTBRIDGE_FIXED_SIZE_DEFAULT;
jobinfo -> ux_pictbridge_jobinfo_cropping =
    UX_PICTBRIDGE_CROPPINGS_DEFAULT;

/* Program the callback function for reading the object data. */
jobinfo -> ux_pictbridge_jobinfo_object_data_read =
    ux_demo_object_data_copy;

/* This is a demo, the fileID is hardwired (1 and 2 for scripts, 3 for photo to be printed. */
printinfo.ux_pictbridge_printinfo_fileid =
    UX_PICTBRIDGE_OBJECT_HANDLE_PRINT;
ux_utility_memory_copy(printinfo.ux_pictbridge_printinfo_filename,
    "Pictbridge demo file", 20);
ux_utility_memory_copy(printinfo.ux_pictbridge_printinfo_date, "01/01/2008",
    10);

/* Fill in the object info to be printed. First get the pointer to the object container in the job info structure. */
object = (UX_SLAVE_CLASS_PIMA_OBJECT *) jobinfo ->
    ux_pictbridge_jobinfo_object;

/* Store the object format: JPEG picture. */
object -> ux_device_class_pima_object_format = UX_DEVICE_CLASS_PIMA_OFC_EXIF_JPEG;
object -> ux_device_class_pima_object_compressed_size = IMAGE_LEN;
object -> ux_device_class_pima_object_offset = 0;
object -> ux_device_class_pima_object_handle_id =
    UX_PICTBRIDGE_OBJECT_HANDLE_PRINT;
object -> ux_device_class_pima_object_length = IMAGE_LEN;

/* File name is in Unicode. */
ux_utility_string_to_unicode("JPEG Image", object ->
    ux_device_class_pima_object_filename);

/* And start the job. */
status =ux_pictbridge_dpsclient_api_start_job(&pictbridge);

现在,Pictbridge 客户端有一个打印作业要执行,它将通过字段中定义的回叫从应用程序中一次提取多个图像块

jobinfo -> ux_pictbridge_jobinfo_object_data_read

此函数的原型定义如下:

ux_pictbridge_jobinfo_object_data_read

从用户空间复制数据块以供打印

原型

UINT ux_pictbridge_jobinfo_object_data_read( 
    UX_PICTBRIDGE *pictbridge,
    UCHAR *object_buffer,  
    ULONG object_offset,  
    ULONG object_length,
    ULONG *actual_length)

说明

当 DPS 客户端需要检索数据块以在目标 Pictbridge 打印机上打印时,就会调用此函数。

参数

  • pictbridge:指向 pictbridge 类实例的指针。
  • object_buffer:指向对象缓冲区的指针
  • object_offset:从哪里开始读取数据块
  • object_length:要返回的长度
  • actual_length:返回的实际长度

返回值

  • UX_SUCCESS (0x00) 此操作成功。
  • UX_ERROR (0x01) 应用程序无法检索数据。

示例

/* Copy the object data. */
UINT ux_demo_object_data_copy(
    UX_PICTBRIDGE *pictbridge,
    UCHAR *object_buffer,
    ULONG object_offset,
    ULONG object_length,
    ULONG *actual_length)
{
    /* Copy the demanded object data portion. */
    ux_utility_memory_copy(object_buffer, image + object_offset,
        object_length);
    /* Update the actual length. */
    *actual_length = object_length;
    /* We have copied the requested data. Return OK. */
    return(UX_SUCCESS);
}

Pictbridge 主机实现

Pictbridge 的主机实现与客户端不同。

在 Pictbridge 主机环境中要做的第一件事是注册 PIMA 类,如下面的示例所示:

status = ux_host_stack_class_register(_ux_system_host_class_pima_name,
    ux_host_class_pima_entry);
if(status != UX_SUCCESS)
    return;

此类是位于 USB 堆栈与 Pictbridge 层之间的通用 PTP 层。

下一步是初始化打印服务的 Pictbridge 默认值,如下所示:

Pictbridge 字段
DpsVersion[0] 0x00010000
DpsVersion[1] 0x00010001
DpsVersion[2] 0x00000000
VendorSpecificVersion 0x00010000
PrintServiceAvailable 0x30010000
Qualities[0] UX_PICTBRIDGE_QUALITIES_DEFAULT
Qualities[1] UX_PICTBRIDGE_QUALITIES_NORMAL
Qualities[2] UX_PICTBRIDGE_QUALITIES_DRAFT
Qualities[3] UX_PICTBRIDGE_QUALITIES_FINE
PaperSizes[0] UX_PICTBRIDGE_PAPER_SIZES_DEFAULT
PaperSizes[1] UX_PICTBRIDGE_PAPER_SIZES_4IX6I
PaperSizes[2] UX_PICTBRIDGE_PAPER_SIZES_L
PaperSizes[3] UX_PICTBRIDGE_PAPER_SIZES_2L
PaperSizes[4] UX_PICTBRIDGE_PAPER_SIZES_LETTER
PaperTypes[0] UX_PICTBRIDGE_PAPER_TYPES_DEFAULT
PaperTypes[1] UX_PICTBRIDGE_PAPER_TYPES_PLAIN
PaperTypes[2 UX_PICTBRIDGE_PAPER_TYPES_PHOTO
FileTypes[0] UX_PICTBRIDGE_FILE_TYPES_DEFAULT
FileTypes[1] UX_PICTBRIDGE_FILE_TYPES_EXIF_JPEG
FileTypes[2] UX_PICTBRIDGE_FILE_TYPES_JFIF
FileTypes[3] UX_PICTBRIDGE_FILE_TYPES_DPOF
DatePrints[0] UX_PICTBRIDGE_DATE_PRINTS_DEFAULT
DatePrints[1] UX_PICTBRIDGE_DATE_PRINTS_OFF
DatePrints[2] UX_PICTBRIDGE_DATE_PRINTS_ON
FileNamePrints[0] UX_PICTBRIDGE_FILE_NAME_PRINTS_DEFAULT
FileNamePrints[1] UX_PICTBRIDGE_FILE_NAME_PRINTS_OFF
FileNamePrints[2] UX_PICTBRIDGE_FILE_NAME_PRINTS_ON
ImageOptimizes[0] UX_PICTBRIDGE_IMAGE_OPTIMIZES_DEFAULT
ImageOptimizes[1] UX_PICTBRIDGE_IMAGE_OPTIMIZES_OFF
ImageOptimizes[2] UX_PICTBRIDGE_IMAGE_OPTIMIZES_ON
Layouts[0] UX_PICTBRIDGE_LAYOUTS_DEFAULT
Layouts[1] UX_PICTBRIDGE_LAYOUTS_1_UP_BORDER
Layouts[2] UX_PICTBRIDGE_LAYOUTS_INDEX_PRINT
Layouts[3] UX_PICTBRIDGE_LAYOUTS_1_UP_BORDERLESS
FixedSizes[0] UX_PICTBRIDGE_FIXED_SIZE_DEFAULT
FixedSizes[1] UX_PICTBRIDGE_FIXED_SIZE_35IX5I
FixedSizes[2] UX_PICTBRIDGE_FIXED_SIZE_4IX6I
FixedSizes[3] UX_PICTBRIDGE_FIXED_SIZE_5IX7I
FixedSizes[4] UX_PICTBRIDGE_FIXED_SIZE_7CMX10CM
FixedSizes[5] UX_PICTBRIDGE_FIXED_SIZE_LETTER
FixedSizes[6] UX_PICTBRIDGE_FIXED_SIZE_A4
Croppings[0] UX_PICTBRIDGE_CROPPINGS_DEFAULT
Croppings[1] UX_PICTBRIDGE_CROPPINGS_OFF
Croppings[2] UX_PICTBRIDGE_CROPPINGS_ON

DPS 主机的状态机将被设置为“空闲”,并准备好接受新的打印作业。

现在可以启动 Pictbridge 的主机部分,如下面的示例所示:

/* Activate the pictbridge dpshost. */
status = ux_pictbridge_dpshost_start(&pictbridge, pima);

if (status != UX_SUCCESS)
    return;

当数据可供打印时,Pictbridge 主机函数需要回叫。 这是通过在 pictbridge 主机结构中传递函数指针来完成的,如下所示。

/* Set a callback when an object is being received. */
pictbridge.ux_pictbridge_application_object_data_write =
    tx_demo_object_data_write;

此函数有以下属性。

ux_pictbridge_application_object_data_write

编写数据块以供打印

原型

UINT ux_pictbridge_application_object_data_write(
    UX_PICTBRIDGE *pictbridge, 
    UCHAR *object_buffer,
    ULONG offset,
    ULONG total_length,
    ULONG length);

说明

当 DPS 服务器需要从 DPS 客户端中检索数据块以在本地打印机上打印时,就会调用此函数。

参数

  • pictbridge:指向 pictbridge 类实例的指针。
  • object_buffer:指向对象缓冲区的指针
  • object_offset:从哪里开始读取数据块
  • total_length:对象的整个长度
  • length:此缓冲区的长度

返回值

  • UX_SUCCESS (0x00) 此操作成功。
  • UX_ERROR (0x01) 应用程序无法打印数据。

示例

/* Copy the object data. */
UINT tx_demo_object_data_write(UX_PICTBRIDGE *pictbridge,
    UCHAR *object_buffer, ULONG offset, ULONG total_length, ULONG length);
{
    UINT status;
    /* Send the data to the local printer. */
    status = local_printer_data_send(object_buffer, length);

    /* We have printed the requested data. Return status. */
    return(status);
}