CAPS Universe documentation  1.0.4
All you need to know to be successful
Macros | Functions
ql-protocol.c File Reference

Printer data protocol related functions shared by all QL label printer drivers. More...

Macros

#define QL_NO_COMPRESSION   0x00
 
#define QL_TIFF_COMPRESSION   0x02
 
#define QL_BI_COLOR   0x01
 
#define QL_CUT_AT_END   0x08
 
#define QL_HIGH_RESOLUTION   0x40
 
#define QL_AUTO_CUT   0x40
 
#define QL_CUT_EACH_LABEL   0
 
#define QL_WHAT_IS_BIT_0   0x01
 
#define QL_MEDIA_ENTRY_VALID   0x02
 
#define QL_WIDTH_ENTRY_VALID   0x04
 
#define QL_LENGTH_ENTRY_VALID   0x08
 
#define QL_RASTER_ENTRY_VALID   0x10
 
#define QL_HIGH_PRINT_QUALITY   0x40
 
#define QL_RECOVER_ENTRY_VALID   0x80
 
#define QL_MEDIA_TYPE_CONTINUOUS   0xa
 
#define QL_MEDIA_TYPE_DIE_CUT   0xb
 
#define QL_QUALITY_PRINT   0x40
 
#define QL_FAST_PRINT   0x00
 

Functions

int ql_page_print (struct ql_drv *t)
 
int ql_last_page_print (struct ql_drv *t)
 
static int __wur ql_compression_setup (struct ql_drv *t, unsigned mode)
 
int ql_reset (struct ql_drv *t)
 
int ql_invalidate_and_initialize (struct ql_drv *t)
 
static __wur int ql_extended_mode_set (struct ql_drv *t, unsigned value)
 
static __wur int ql_cut_mode_set (struct ql_drv *t, unsigned mode)
 
static __wur int ql_cut_on_label_n (struct ql_drv *t, unsigned cut_on_page)
 
static __wur int ql_notification_disable (struct ql_drv *t)
 
static __wur int _ql_status_request (struct ql_drv *t)
 
int ql_status_request (struct ql_drv *t, unsigned char buffer[32])
 
static __wur int ql_dynamic_mode (struct ql_drv *t)
 
static __wur int ql_define_tape_margins (struct ql_drv *t, uint16_t margins)
 
static __wur int ql_print_information (struct ql_drv *t, int media_type, int media_width, int media_length, int raster, int start_page, int quality)
 
int ql_job_header_generate (struct ql_drv *t)
 
int ql_page_header_generate (struct ql_drv *t, int first_page)
 
int ql_auto_powerdown (struct ql_drv *t, unsigned short time_out)
 
int ql_auto_powerup (struct ql_drv *t, unsigned short mode)
 

Detailed Description

A regular print job for a QL printer:

ql_define_tape_margins()

Macro Definition Documentation

◆ QL_NO_COMPRESSION

#define QL_NO_COMPRESSION   0x00

Value to signal the printer, the print data will be sent without any compression. Used in the ql_compression_setup() function as its mode parameter

◆ QL_TIFF_COMPRESSION

#define QL_TIFF_COMPRESSION   0x02

Value to signal the printer, the print data will be sent in TIFF compression. Used in the ql_compression_setup() function as its mode parameter

Attention
Only for reference. Compression isn't supported, yet

◆ QL_BI_COLOR

#define QL_BI_COLOR   0x01

Bit to setup the printer into bi-colour print mode. Used in the ql_extended_mode_set() function as its value parameter

Note
Can be or'ed with the QL_CUT_AT_END and QL_HIGH_RESOLUTION bits.

◆ QL_CUT_AT_END

#define QL_CUT_AT_END   0x08

Bit to setup the printer into "cut when job is finished" mode. Used in the ql_extended_mode_set() function as its value parameter

For: QL-570/580N/700/QL8xx

Note
It is still unclear what exactly this bit means in conjunction to the other cutting related bits. If someone has an idea, drop me a note.
Can be or'ed with the QL_BI_COLOR and QL_HIGH_RESOLUTION bits.

◆ QL_HIGH_RESOLUTION

#define QL_HIGH_RESOLUTION   0x40

Bit to setup the printer into "high resolution" mode. E.g. 600 DPI in vertical direction. Used in the ql_extended_mode_set() function as its value parameter

Note
Can be or'ed with the QL_BI_COLOR and QL_CUT_AT_END bits.

◆ QL_AUTO_CUT

#define QL_AUTO_CUT   0x40

Bit to setup the printer into "auto cut" mode. Used in the ql_cut_mode_set() function as its mode parameter For QL550/560/570/580N/650TD/700/1050/1060N

◆ QL_CUT_EACH_LABEL

#define QL_CUT_EACH_LABEL   0

Default value for the ql_cut_on_label_n() function to define "cut each single label" To be used as its cut_on_page parameter.

◆ QL_WHAT_IS_BIT_0

#define QL_WHAT_IS_BIT_0   0x01

Unknown meaning in the #print_inform::valid member

◆ QL_MEDIA_ENTRY_VALID

#define QL_MEDIA_ENTRY_VALID   0x02

◆ QL_WIDTH_ENTRY_VALID

#define QL_WIDTH_ENTRY_VALID   0x04

◆ QL_LENGTH_ENTRY_VALID

#define QL_LENGTH_ENTRY_VALID   0x08

◆ QL_RASTER_ENTRY_VALID

#define QL_RASTER_ENTRY_VALID   0x10

◆ QL_HIGH_PRINT_QUALITY

#define QL_HIGH_PRINT_QUALITY   0x40

◆ QL_RECOVER_ENTRY_VALID

#define QL_RECOVER_ENTRY_VALID   0x80

I found a hint. When set, it means: only print on labels of matching size

◆ QL_MEDIA_TYPE_CONTINUOUS

#define QL_MEDIA_TYPE_CONTINUOUS   0xa

◆ QL_MEDIA_TYPE_DIE_CUT

#define QL_MEDIA_TYPE_DIE_CUT   0xb

◆ QL_QUALITY_PRINT

#define QL_QUALITY_PRINT   0x40

never use while bi-colour printing (according to the datasheet)

◆ QL_FAST_PRINT

#define QL_FAST_PRINT   0x00

Always use with bi-colour printing (according to the datasheet)

Function Documentation

◆ ql_page_print()

int ql_page_print ( struct ql_drv t)

Finish the current page

Parameters
[in]tFull job description
Return values
0On success
NegativeError code from the ql_driver_data_send() call

This code is to be sent after a page.

It seems, this command is only to be sent, if more than one page in one job should be printed. If the job contains only one page, the ql_last_page_print() is enough to do the right thing.

Note
To be sent at the end of a page's raster, if it isn't the last page to be print (according to the manual)

◆ ql_last_page_print()

int ql_last_page_print ( struct ql_drv t)

Finish the current print job

Parameters
[in]tFull job description
Return values
0On success
NegativeError code from the ql_driver_data_send() call

This code is to be sent after the last page to finish the whole print job.

Note
To be sent at the end of a page's raster, if it is the last page of the job (according to the manual)

◆ ql_compression_setup()

static int __wur ql_compression_setup ( struct ql_drv t,
unsigned  mode 
)
static

Setup the line data compression mode

Parameters
[in]tFull job description
[in]modeCompression more, one of QL_NO_COMPRESSION or QL_TIFF_COMPRESSION
Return values
0On success
NegativeError code from the ql_driver_data_send() call
Note
Only a few printers can deal with compression, so QL_TIFF_COMPRESSION isn't supported, yet.
To be sent at the beginning of a page's raster and required at each page (according to the manual)

◆ ql_reset()

int ql_reset ( struct ql_drv t)

Initialize/reset the printer

Parameters
[in]tFull job description
Return values
0On success
NegativeError code from the ql_driver_data_send() call

This command can also be used every time to terminate a current print

Note
To be sent once at the beginning of a print job (according to the manual)

◆ ql_invalidate_and_initialize()

int ql_invalidate_and_initialize ( struct ql_drv t)

Start a new print job from the printer's point of view

Parameters
[in]tFull job description
Return values
0On success
NegativeError code from the ql_driver_data_send() call

To be sent at the beginning of a new print job or if the current processing has to be stopped at the printer's side.

From the datasheet: "If data transmission is to be stopped midway, send the “initialize” command after sending the “invalidate” command for the appropriate number of bytes to return to the receiving state, where the print buffer is cleared."

Note
invalidate means the specified amount of zero bytes
initialize means the *ESC,@* command in ql_reset()
To be sent once at the beginning of a print job (according to the manual)

◆ ql_extended_mode_set()

static __wur int ql_extended_mode_set ( struct ql_drv t,
unsigned  value 
)
static

Send expanded mode

Parameters
[in]tFull job description
[in]valueOred list of of QL_BI_COLOR, QL_CUT_AT_END or QL_HIGH_RESOLUTION
Return values
0On success
NegativeError code from the ql_driver_data_send() call
Note
With the cutter related bit 3 cutting at the end (whatever 'end' here is meant) can be controlled
To be sent at the beginning of a page and required at each page (according to the manual)

◆ ql_cut_mode_set()

static __wur int ql_cut_mode_set ( struct ql_drv t,
unsigned  mode 
)
static

Setup "various mode"

Parameters
[in]tFull job description
[in]modeCutter mode, '0' or QL_AUTO_CUT
Return values
0On success
NegativeError code from the ql_driver_data_send() call
Attention
When this function/command is used to enable "auto cut", the ql_cut_on_label_n() must be called as well.

Older documentation defines the bits of mode to:

  • bit 7: Mirror print (0 = do not mirror)
  • bit 6: Auto cut (0 = no auto cut)
  • bit 5: unknown
  • bit 0…4: Feed amount (0-7=none, 8-11=small, 12-25=medium, 26-31=large)
Note
This information might be valid for ptouch printers and not for QL printers (with the auto cut feature as an exception)
To be sent at the beginning of a page and required at each page (according to the manual)

◆ ql_cut_on_label_n()

static __wur int ql_cut_on_label_n ( struct ql_drv t,
unsigned  cut_on_page 
)
static

Setup the label count when to cut (makes sense only if auto-cut is enabled)

Parameters
[in]tFull job description
[in]cut_on_pageAuto cut after this amount of pages (can be QL_CUT_EACH_LABEL for cut every label)
Return values
0On success
NegativeError code from the ql_driver_data_send() call
Attention
Makes sense only if auto-cut is enabled and with pre-cut labels
Note
Nothing happens (at least with the cutter), if this command wasn't sent.
To be sent at the beginning of a page and required at each page (according to the manual)
Precondition
A call to ql_cut_mode_set() which enables "auto cut"

◆ ql_notification_disable()

static __wur int ql_notification_disable ( struct ql_drv t)
static

Disable the printer's notification capability

Parameters
[in]tFull job description
Return values
0On success
NegativeError code from the ql_driver_data_send() call

We do not have a real back channel, so we can't recieve its data

Todo:
But it would be useful to have a back channel!

◆ _ql_status_request()

static __wur int _ql_status_request ( struct ql_drv t)
static

Request status report from printer.

Parameters
[in]tFull job description

After sending this command, it:

  • takes a while until the data hits the printer
    • it must go through the system's memory
    • it must go through the USB subsystem
    • it must go over the USB line
    • it must be processed by the printer
  • takes a while until the printer's respond hits our memory
    • the USB controller must request the printer for data
    • the data must go over the USB line
    • the data must go through the USB subsystem
    • the data must go through the system's memory

Sometimes this request fails:

  • at least if the printer is in trouble due to bad data or empty cassette.
    • you see the red LED blinking slowly in this case
      • the printer's USB interface status in this case is: error=no and or paper empty=1
    • it responds with a report in this case without a request
      • this report is of type "phase change"
    • after this last report, it doesn't respond to requests anymore
    • after opening and closing the cover manually it responds again on a request
Attention
Until the data hits our memory the printer's filedescriptor
is in state EOF and for example a select() on it does't work!
If the printer is in trouble (e.g. error state), sending this command string will block!

◆ ql_status_request()

int ql_status_request ( struct ql_drv t,
unsigned char  buffer[32] 
)

Request and read status from printer.

Parameters
[in]tFull job description
[out]bufferStorage for the 32 status bytes read from the printer
Return values
0Status read successfully
-ENODATANo status information from the printer. buffer content is invalid
-ENODEVIf the printer is gone in the meanwhile. This is serious!
-errnoAll error values from ql_driver_data_send(). They are serious!

Some run-time experiences:

  • the printer reports always the inserted cassette, even if the cover is opened (QL800 tested)
  • if no cassette is inserted, it returns 0x00 as the 'media_type' and width and length as 0.
  • whenever an error occur, the report is marked as an error report (field 'status_type')
  • if the cover is closed but whithout an inserted cassette the report isn't marked as an error report
  • it seems, the 'automatic status notification mode' works differently. At my side it didn't work at all
Precondition
Printer must be idle

◆ ql_dynamic_mode()

static __wur int ql_dynamic_mode ( struct ql_drv t)
static

Configure the printer to a sepcifc "dynamic mode"

Parameters
[in]tFull job description
Return values
0On success
NegativeError code from the ql_driver_data_send() call

For some QL models this has to be done at most once per run-time. The printer operates in this mode until it is turned off again. But read the note below!

Note
No rule without exception: according to its datasheet, the QL600 requires this command right after the reset command and requires it again after the ql_last_page_print() but with mode 'default' (eg. mode = 0xff)
To be sent at the beginning of a page and required at each page (according to the manual). The real requirement is unclear, since the datasheet lists different requirements at different pages.

◆ ql_define_tape_margins()

static __wur int ql_define_tape_margins ( struct ql_drv t,
uint16_t  margins 
)
static

Setup the margins (top and bottom) to be used for this print

Parameters
[in]tFull job description
[in]marginsTop and bottom margins [dots] TODO maybe better in mm?
Return values
0On success
NegativeError code from the ql_driver_data_send() call
Precondition
According to the QL800 manual, the margins must be at least 3 mm (or 35 dots) and max. 127 mm (or 1500 dots).
Attention
Since the margin is defined in dots, it is resolution dependend (vertical!)
Note
From the manual: "since a margin amount cannot be specified with die-cut labels, this command is sent with a margin amount of 0."
(from section 2.2.3 Explanation of print data for the test page)
 Continuous tape:

            cut                                          cut
  -----------|--------------------------------------------|--------------
         |   |   |                                    |   |   |
   ----->|   |   |<------- printable area ----------->|   |   |<---------
         |   |   |                                    |   |   |
  -----------|--------------------------------------------|--------------
         |<->|<->| < margins                margins > |<->|<->|

 Die-cut labels (assumption, but was wrong!):

            cut                                           cut
  -----------|--------------------------------------------|-------------------
  -------+   |   +------------------------------------+   |   +---------------
         |   |   |                                    |   |   |
  ------>|   |   |<---------- printable area -------->|   |   |<--------------
         |   |   |                                    |   |   |
  -------+   |   +------------------------------------+   |   +---+-----------
  -----------|--------------------------------------------|-------------------

 Die-cut labels, margin handling (reality):

            cut                                           cut
  -----------|--------------------------------------------|-------------------
  -------+   |   +------------------------------------+   |   +---------------
         |   |   |  |                              |  |   |   |
  ------>|   |   |  |<------- printable area ----->|  |   |   |<--------------
         |   |   |  |                              |  |   |   |
  -------+   |   +------------------------------------+   |   +---+-----------
  -----------|--------------------------------------------|-------------------
             |<->|<>| < margins          margins > |<>|<->|
                                                        ^---- implicit margin added by the printer
                                                     ^------- implicit margin added by the printer
* 
Attention
The top and bottom margins of 35 dots are implicit for die-cut labels. The printer doesn't print in these areas, even if data was sent for them.
It is even more worse: data sent for these areas (e.g. lines of data) will not be ignored. The printer increases the size of the label instead.
And the next cut is made inside the following label - instead of inbetween them.
Note
To be sent at the beginning of a page and required at each page (according to the manual)

◆ ql_print_information()

static __wur int ql_print_information ( struct ql_drv t,
int  media_type,
int  media_width,
int  media_length,
int  raster,
int  start_page,
int  quality 
)
static

Send information about the job or the next page

Parameters
[in]tFull job description
[in]media_typeQL_MEDIA_TYPE_CONTINUOUS or QL_MEDIA_TYPE_DIE_CUT, negative value for not defined
[in]media_widthWidth of the to be printed medium in [mm], negative value for not defined
[in]media_lengthLength of the to be printed media in [mm] ('0' for continuous roll), negative value for not defined
[in]rasterCount of rasterlines of one "page", negative value for not defined
[in]start_page'0' for the first page, '1' for continuous page
[in]qualityQL_QUALITY_PRINT or QL_FAST_PRINT
Return values
0On success
NegativeError code from the ql_driver_data_send() call

The manual encodes this info (as an example):

This is the command for “1.1" × 3.5" (29 mm × 90 mm)” die-cut labels.

into:

 1b 69 7A 8F 0B 1D 5A DF 03 00 00 00 00
                                  ^^___ Starting page
                      ^^_^^_^^_^^______ 991 raster lines ~83 mm
                   ^^__________________ 90 mm length
                ^^_____________________ 29 mm width
             ^^________________________ Media type: die cut
          ^^___________________________ 8F: 10x0 1111
                                                    ^_ ????
                                                   ^__ Media type valid
                                                  ^___ Width valid
                                                 ^____ Length valid
                                               ^______ Raster invalid (vague interpretation!)
                                             ^________ fast print
                                            ^_________ Recovery

For the DK1201 roll (die-cut 29 mm x 90 mm label), the QL800 datasheet defines:

  • width: 342 dots
  • heigt: 1061 dots (= top margin + printable height + top/bottom margin)
  • printable width: 306 dots
  • printable height: 991 dots
  • left/right margin: 18 dots
  • top/bottom margin: 35 dots Note: all at 300 DPI
Note
To be sent at the beginning of a page and required at each page (according to the manual)

◆ ql_job_header_generate()

int ql_job_header_generate ( struct ql_drv t)

Send the heading commands to setup the printer prior a print job

Parameters
[in]tFull job description

Called once per print job at the beginning

◆ ql_page_header_generate()

int ql_page_header_generate ( struct ql_drv t,
int  first_page 
)
Parameters
[in]tFull job description
[in]first_pageQL_FIRST_PAGE if it is the page header for the first page, QL_NEXT_PAGE else
Return values
0On success
-ENODEVDevice seems gone (e.g. channel to the next stage is gone)

Called once per page at the beginning

Note
In case of -ENODEV it makes no sense trying to send any further data or commands
Precondition
The ql_job::trim member must be up to date, e.g. its content matches the current raster page header
Todo:

Deal with QL_FAST_PRINT/QL_QUALITY_PRINT for monochrome use case

Do the QL_QUALITY_PRINT and QL_HIGH_RESOLUTION bits depend on each other?

Deal with auto cut setting and when to cut (and if the printer has a cutter). To make it simple: only "cut at end" and "cut each" should be supported

◆ ql_auto_powerdown()

int ql_auto_powerdown ( struct ql_drv t,
unsigned short  time_out 
)
Note
This command is reverse-engineered, it isn't listed in the programming manual
The big endian format for the subcommand's data (here time_val) is guessed only. All other printer commands use little endian for multibyte parameter. So, the command's data might have a different format/content than listed here

◆ ql_auto_powerup()

int ql_auto_powerup ( struct ql_drv t,
unsigned short  mode 
)
Note
This command is reverse-engineered, it isn't listed in the programming manual
The big endian format for the subcommand's data (here mode) is guessed only. All other commands use little endian for multibyte parameter. So, the command's data might have a different format/content than listed here