1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * Copyright (C) 2016 Mopria Alliance, Inc. 4 * Copyright (C) 2013 Hewlett-Packard Development Company, L.P. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <sys/stat.h> 22 #include <fcntl.h> 23 24 #ifndef _GNU_SOURCE 25 #define _GNU_SOURCE 26 #endif 27 #ifndef __USE_UNIX98 28 #define __USE_UNIX98 29 #endif 30 31 #include <pthread.h> 32 33 #include <semaphore.h> 34 #include <printer_capabilities_types.h> 35 36 #include "ifc_print_job.h" 37 #include "wprint_debug.h" 38 #include "plugin_db.h" 39 40 #include "ifc_status_monitor.h" 41 42 #include "ippstatus_monitor.h" 43 #include "ippstatus_capabilities.h" 44 #include "ipp_print.h" 45 #include "ipphelper.h" 46 47 #include "lib_printable_area.h" 48 #include "wprint_io_plugin.h" 49 #include "../plugins/media.h" 50 51 #define TAG "lib_wprint" 52 53 /* As expected by target devices */ 54 #define USERAGENT_PREFIX "wPrintAndroid" 55 56 #define USE_PWG_OVER_PCLM 0 57 58 #if (USE_PWG_OVER_PCLM != 0) 59 #define _DEFAULT_PRINT_FORMAT PRINT_FORMAT_PWG 60 #define _DEFAULT_PCL_TYPE PCLPWG 61 #else // (USE_PWG_OVER_PCLM != 0) 62 #define _DEFAULT_PRINT_FORMAT PRINT_FORMAT_PCLM 63 #define _DEFAULT_PCL_TYPE PCLm 64 #endif // (USE_PWG_OVER_PCLM != 0) 65 66 #define _MAX_SPOOLED_JOBS 100 67 #define _MAX_MSGS (_MAX_SPOOLED_JOBS * 5) 68 69 #define _MAX_PAGES_PER_JOB 1000 70 71 #define MAX_IDLE_WAIT (5 * 60) 72 73 #define DEFAULT_RESOLUTION (300) 74 75 // When searching for a supported resolution this is the max resolution we will consider. 76 #define MAX_SUPPORTED_RESOLUTION (720) 77 78 #define MAX_DONE_WAIT (5 * 60) 79 #define MAX_START_WAIT (45) 80 81 #define IO_PORT_FILE 0 82 83 /* 84 * The following macros allow for up to 8 bits (256) for spooled job id#s and 85 * 24 bits (16 million) of a running sequence number to provide a reasonably 86 * unique job handle 87 */ 88 89 // _ENCODE_HANDLE() is only called from _get_handle() 90 #define _ENCODE_HANDLE(X) ( (((++_running_number) & 0xffffff) << 8) | ((X) & 0xff) ) 91 #define _DECODE_HANDLE(X) ((X) & 0xff) 92 93 #undef snprintf 94 #undef vsnprintf 95 96 typedef enum { 97 JOB_STATE_FREE, // queue element free 98 JOB_STATE_QUEUED, // job queued and waiting to be run 99 JOB_STATE_RUNNING, // job running (printing) 100 JOB_STATE_BLOCKED, // print job blocked due to printer stall/error 101 JOB_STATE_CANCEL_REQUEST, // print job cancelled by user, 102 JOB_STATE_CANCELLED, // print job cancelled by user, waiting to be freed 103 JOB_STATE_COMPLETED, // print job completed successfully, waiting to be freed 104 JOB_STATE_ERROR, // job could not be run due to error 105 JOB_STATE_CORRUPTED, // job could not be run due to error 106 107 NUM_JOB_STATES 108 } _job_state_t; 109 110 typedef enum { 111 TOP_MARGIN = 0, 112 LEFT_MARGIN, 113 RIGHT_MARGIN, 114 BOTTOM_MARGIN, 115 116 NUM_PAGE_MARGINS 117 } _page_margins_t; 118 119 typedef enum { 120 MSG_RUN_JOB, MSG_QUIT, 121 } wprint_msg_t; 122 123 typedef struct { 124 wprint_msg_t id; 125 wJob_t job_id; 126 } _msg_t; 127 128 /* 129 * Define an entry in the job queue 130 */ 131 typedef struct { 132 wJob_t job_handle; 133 _job_state_t job_state; 134 unsigned int blocked_reasons; 135 wprint_status_cb_t cb_fn; 136 char *printer_addr; 137 port_t port_num; 138 wprint_plugin_t *plugin; 139 ifc_print_job_t *print_ifc; 140 char *mime_type; 141 char *pathname; 142 bool is_dir; 143 bool last_page_seen; 144 int num_pages; 145 msg_q_id pageQ; 146 msg_q_id saveQ; 147 148 wprint_job_params_t job_params; 149 bool cancel_ok; 150 151 const ifc_status_monitor_t *status_ifc; 152 char debug_path[MAX_PATHNAME_LENGTH + 1]; 153 char printer_uri[1024]; 154 int job_debug_fd; 155 int page_debug_fd; 156 } _job_queue_t; 157 158 /* 159 * An entry for queued pages 160 */ 161 typedef struct { 162 int page_num; 163 bool pdf_page; 164 bool last_page; 165 bool corrupted; 166 char filename[MAX_PATHNAME_LENGTH + 1]; 167 unsigned int top_margin; 168 unsigned int left_margin; 169 unsigned int right_margin; 170 unsigned int bottom_margin; 171 } _page_t; 172 173 /* 174 * Entry for a registered plugin 175 */ 176 typedef struct { 177 port_t port_num; 178 const wprint_io_plugin_t *io_plugin; 179 } _io_plugin_t; 180 181 static _job_queue_t _job_queue[_MAX_SPOOLED_JOBS]; 182 static msg_q_id _msgQ; 183 184 static pthread_t _job_status_tid; 185 static pthread_t _job_tid; 186 187 static pthread_mutex_t _q_lock; 188 static pthread_mutexattr_t _q_lock_attr; 189 190 static sem_t _job_end_wait_sem; 191 static sem_t _job_start_wait_sem; 192 193 static _io_plugin_t _io_plugins[2]; 194 195 char g_osName[MAX_ID_STRING_LENGTH + 1] = {0}; 196 char g_appName[MAX_ID_STRING_LENGTH + 1] = {0}; 197 char g_appVersion[MAX_ID_STRING_LENGTH + 1] = {0}; 198 199 /* 200 * Convert a pcl_t type to a human-readable string 201 */ 202 static char *getPCLTypeString(pcl_t pclenum) { 203 switch (pclenum) { 204 case PCLNONE: 205 return "PCL_NONE"; 206 case PCLm: 207 return "PCLm"; 208 case PCLJPEG: 209 return "PCL_JPEG"; 210 case PCLPWG: 211 return "PWG-Raster"; 212 default: 213 return "unkonwn PCL Type"; 214 } 215 } 216 217 /* 218 * Return a _job_queue_t item by its job_handle or NULL if not found. 219 */ 220 static _job_queue_t *_get_job_desc(wJob_t job_handle) { 221 unsigned long index; 222 if (job_handle == WPRINT_BAD_JOB_HANDLE) { 223 return NULL; 224 } 225 index = _DECODE_HANDLE(job_handle); 226 if ((index < _MAX_SPOOLED_JOBS) && (_job_queue[index].job_handle == job_handle) && 227 (_job_queue[index].job_state != JOB_STATE_FREE)) { 228 return (&_job_queue[index]); 229 } else { 230 return NULL; 231 } 232 } 233 234 /* 235 * Functions below to fill out the _debug_stream_ifc interface 236 */ 237 238 static void _stream_dbg_end_job(wJob_t job_handle) { 239 _job_queue_t *jq = _get_job_desc(job_handle); 240 if (jq && (jq->job_debug_fd >= 0)) { 241 close(jq->job_debug_fd); 242 jq->job_debug_fd = -1; 243 } 244 } 245 246 static void _stream_dbg_start_job(wJob_t job_handle, const char *ext) { 247 _stream_dbg_end_job(job_handle); 248 _job_queue_t *jq = _get_job_desc(job_handle); 249 if (jq && jq->debug_path[0]) { 250 char filename[MAX_PATHNAME_LENGTH + 1]; 251 snprintf(filename, MAX_PATHNAME_LENGTH, "%s/jobstream.%s", jq->debug_path, ext); 252 filename[MAX_PATHNAME_LENGTH] = 0; 253 jq->job_debug_fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); 254 } 255 } 256 257 static void _stream_dbg_job_data(wJob_t job_handle, const unsigned char *buff, 258 unsigned long nbytes) { 259 _job_queue_t *jq = _get_job_desc(job_handle); 260 ssize_t bytes_written; 261 if (jq && (jq->job_debug_fd >= 0)) { 262 while (nbytes > 0) { 263 bytes_written = write(jq->job_debug_fd, buff, nbytes); 264 if (bytes_written < 0) { 265 return; 266 } 267 nbytes -= bytes_written; 268 buff += bytes_written; 269 } 270 } 271 } 272 273 static void _stream_dbg_end_page(wJob_t job_handle) { 274 _job_queue_t *jq = _get_job_desc(job_handle); 275 if (jq && (jq->page_debug_fd >= 0)) { 276 close(jq->page_debug_fd); 277 jq->page_debug_fd = -1; 278 } 279 } 280 281 static void _stream_dbg_page_data(wJob_t job_handle, const unsigned char *buff, 282 unsigned long nbytes) { 283 _job_queue_t *jq = _get_job_desc(job_handle); 284 ssize_t bytes_written; 285 if (jq && (jq->page_debug_fd >= 0)) { 286 while (nbytes > 0) { 287 bytes_written = write(jq->page_debug_fd, buff, nbytes); 288 if (bytes_written < 0) { 289 return; 290 } 291 nbytes -= bytes_written; 292 buff += bytes_written; 293 } 294 } 295 } 296 297 #define PPM_IDENTIFIER "P6\n" 298 #define PPM_HEADER_LENGTH 128 299 300 static void _stream_dbg_start_page(wJob_t job_handle, int page_number, int width, int height) { 301 _stream_dbg_end_page(job_handle); 302 _job_queue_t *jq = _get_job_desc(job_handle); 303 if (jq && jq->debug_path[0]) { 304 union { 305 char filename[MAX_PATHNAME_LENGTH + 1]; 306 char ppm_header[PPM_HEADER_LENGTH + 1]; 307 } buff; 308 snprintf(buff.filename, MAX_PATHNAME_LENGTH, "%s/page%4.4d.ppm", jq->debug_path, 309 page_number); 310 buff.filename[MAX_PATHNAME_LENGTH] = 0; 311 jq->page_debug_fd = open(buff.filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); 312 int length = snprintf(buff.ppm_header, sizeof(buff.ppm_header), "%s\n#%*c\n%d %d\n%d\n", 313 PPM_IDENTIFIER, 0, ' ', width, height, 255); 314 int padding = sizeof(buff.ppm_header) - length; 315 snprintf(buff.ppm_header, sizeof(buff.ppm_header), "%s\n#%*c\n%d %d\n%d\n", 316 PPM_IDENTIFIER, padding, ' ', width, height, 255); 317 _stream_dbg_page_data(job_handle, (const unsigned char *) buff.ppm_header, 318 PPM_HEADER_LENGTH); 319 } 320 } 321 322 static const ifc_wprint_debug_stream_t _debug_stream_ifc = { 323 .debug_start_job = _stream_dbg_start_job, .debug_job_data = _stream_dbg_job_data, 324 .debug_end_job = _stream_dbg_end_job, .debug_start_page = _stream_dbg_start_page, 325 .debug_page_data = _stream_dbg_page_data, .debug_end_page = _stream_dbg_end_page 326 }; 327 328 /* 329 * Return the debug stream interface corresponding to the specified job handle 330 */ 331 const ifc_wprint_debug_stream_t *getDebugStreamIfc(wJob_t handle) { 332 _job_queue_t *jq = _get_job_desc(handle); 333 if (jq) { 334 return (jq->debug_path[0] == 0) ? NULL : &_debug_stream_ifc; 335 } 336 return NULL; 337 } 338 339 const ifc_wprint_t _wprint_ifc = { 340 .msgQCreate = msgQCreate, .msgQDelete = msgQDelete, 341 .msgQSend = msgQSend, .msgQReceive = msgQReceive, .msgQNumMsgs = msgQNumMsgs, 342 .get_debug_stream_ifc = getDebugStreamIfc 343 }; 344 345 static pcl_t _default_pcl_type = _DEFAULT_PCL_TYPE; 346 347 static const ifc_print_job_t *_printer_file_connect(const ifc_wprint_t *wprint_ifc) { 348 return printer_connect(IO_PORT_FILE); 349 } 350 351 static const ifc_printer_capabilities_t *_get_caps_ifc(port_t port_num) { 352 int i; 353 for (i = 0; i < ARRAY_SIZE(_io_plugins); i++) { 354 if (_io_plugins[i].port_num == port_num) { 355 if (_io_plugins[i].io_plugin == NULL) { 356 return NULL; 357 } 358 if (_io_plugins[i].io_plugin->getCapsIFC == NULL) { 359 return NULL; 360 } else { 361 return (_io_plugins[i].io_plugin->getCapsIFC(&_wprint_ifc)); 362 } 363 } 364 } 365 return NULL; 366 } 367 368 static const ifc_status_monitor_t *_get_status_ifc(port_t port_num) { 369 int i; 370 for (i = 0; i < ARRAY_SIZE(_io_plugins); i++) { 371 if (_io_plugins[i].port_num == port_num) { 372 if (_io_plugins[i].io_plugin == NULL) { 373 return NULL; 374 } 375 if (_io_plugins[i].io_plugin->getStatusIFC == NULL) { 376 return NULL; 377 } else { 378 return (_io_plugins[i].io_plugin->getStatusIFC(&_wprint_ifc)); 379 } 380 } 381 } 382 return NULL; 383 } 384 385 static const ifc_print_job_t *_get_print_ifc(port_t port_num) { 386 int i; 387 for (i = 0; i < ARRAY_SIZE(_io_plugins); i++) { 388 if (_io_plugins[i].port_num == port_num) { 389 if (_io_plugins[i].io_plugin == NULL) { 390 return NULL; 391 } 392 if (_io_plugins[i].io_plugin->getPrintIFC == NULL) { 393 return NULL; 394 } else { 395 return (_io_plugins[i].io_plugin->getPrintIFC(&_wprint_ifc)); 396 } 397 } 398 } 399 return NULL; 400 } 401 402 /* 403 * Lock the semaphore for this module 404 */ 405 static void _lock(void) { 406 pthread_mutex_lock(&_q_lock); 407 } 408 409 /* 410 * Unlock the semaphore for this module 411 */ 412 static void _unlock(void) { 413 pthread_mutex_unlock(&_q_lock); 414 } 415 416 static wJob_t _get_handle(void) { 417 static unsigned long _running_number = 0; 418 wJob_t job_handle = WPRINT_BAD_JOB_HANDLE; 419 int i, index, size; 420 char *ptr; 421 422 for (i = 0; i < _MAX_SPOOLED_JOBS; i++) { 423 index = (i + _running_number) % _MAX_SPOOLED_JOBS; 424 425 if (_job_queue[index].job_state == JOB_STATE_FREE) { 426 size = MAX_MIME_LENGTH + MAX_PRINTER_ADDR_LENGTH + MAX_PATHNAME_LENGTH + 4; 427 ptr = malloc(size); 428 if (ptr) { 429 memset(&_job_queue[index], 0, sizeof(_job_queue_t)); 430 memset(ptr, 0, size); 431 432 _job_queue[index].job_debug_fd = -1; 433 _job_queue[index].page_debug_fd = -1; 434 _job_queue[index].printer_addr = ptr; 435 436 ptr += (MAX_PRINTER_ADDR_LENGTH + 1); 437 _job_queue[index].mime_type = ptr; 438 ptr += (MAX_MIME_LENGTH + 1); 439 _job_queue[index].pathname = ptr; 440 441 _job_queue[index].job_state = JOB_STATE_QUEUED; 442 _job_queue[index].job_handle = _ENCODE_HANDLE(index); 443 444 job_handle = _job_queue[index].job_handle; 445 } 446 break; 447 } 448 } 449 return job_handle; 450 } 451 452 static int _recycle_handle(wJob_t job_handle) { 453 _job_queue_t *jq = _get_job_desc(job_handle); 454 455 if (jq == NULL) { 456 return ERROR; 457 } else if ((jq->job_state == JOB_STATE_CANCELLED) || (jq->job_state == JOB_STATE_ERROR) || 458 (jq->job_state == JOB_STATE_CORRUPTED) || (jq->job_state == JOB_STATE_COMPLETED)) { 459 if (jq->print_ifc != NULL) { 460 jq->print_ifc->destroy(jq->print_ifc); 461 } 462 463 jq->print_ifc = NULL; 464 if (jq->status_ifc != NULL) { 465 jq->status_ifc->destroy(jq->status_ifc); 466 } 467 jq->status_ifc = NULL; 468 if (jq->job_params.useragent != NULL) { 469 free((void *) jq->job_params.useragent); 470 } 471 free(jq->printer_addr); 472 jq->job_state = JOB_STATE_FREE; 473 if (jq->job_debug_fd != -1) { 474 close(jq->job_debug_fd); 475 } 476 jq->job_debug_fd = -1; 477 if (jq->page_debug_fd != -1) { 478 close(jq->page_debug_fd); 479 } 480 jq->page_debug_fd = -1; 481 jq->debug_path[0] = 0; 482 483 return OK; 484 } else { 485 return ERROR; 486 } 487 } 488 489 /* 490 * Stops the job status thread if it exists 491 */ 492 static int _stop_status_thread(_job_queue_t *jq) { 493 if (!pthread_equal(_job_status_tid, pthread_self()) && (jq && jq->status_ifc)) { 494 (jq->status_ifc->stop)(jq->status_ifc); 495 _unlock(); 496 pthread_join(_job_status_tid, 0); 497 _lock(); 498 _job_status_tid = pthread_self(); 499 return OK; 500 } else { 501 return ERROR; 502 } 503 } 504 505 /* 506 * Handles a new status message from the printer. Based on the status of wprint and the printer, 507 * this function will start/end a job, send another page, or return blocking errors. 508 */ 509 static void _job_status_callback(const printer_state_dyn_t *new_status, 510 const printer_state_dyn_t *old_status, void *param) { 511 wprint_job_callback_params_t cb_param; 512 _job_queue_t *jq = (_job_queue_t *) param; 513 unsigned int i, blocked_reasons; 514 print_status_t statusnew, statusold; 515 516 statusnew = new_status->printer_status & ~PRINTER_IDLE_BIT; 517 statusold = old_status->printer_status & ~PRINTER_IDLE_BIT; 518 519 LOGD("_job_status_callback(): current printer state: %d", statusnew); 520 blocked_reasons = 0; 521 for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) { 522 if (new_status->printer_reasons[i] == PRINT_STATUS_MAX_STATE) { 523 break; 524 } 525 LOGD("_job_status_callback(): blocking reason %d: %d", i, new_status->printer_reasons[i]); 526 blocked_reasons |= (1 << new_status->printer_reasons[i]); 527 } 528 529 switch (statusnew) { 530 case PRINT_STATUS_UNKNOWN: 531 if ((new_status->printer_reasons[0] == PRINT_STATUS_OFFLINE) 532 || (new_status->printer_reasons[0] == PRINT_STATUS_UNKNOWN)) { 533 sem_post(&_job_start_wait_sem); 534 sem_post(&_job_end_wait_sem); 535 _lock(); 536 if ((new_status->printer_reasons[0] == PRINT_STATUS_OFFLINE) 537 && ((jq->print_ifc != NULL) && (jq->print_ifc->enable_timeout != NULL))) { 538 jq->print_ifc->enable_timeout(jq->print_ifc, 1); 539 } 540 _unlock(); 541 } 542 break; 543 544 case PRINT_STATUS_IDLE: 545 if ((statusold > PRINT_STATUS_IDLE) || (statusold == PRINT_STATUS_CANCELLED)) { 546 // Print is over but the job wasn't ended correctly 547 if (jq->is_dir && !jq->last_page_seen) { 548 wprintPage(jq->job_handle, jq->num_pages + 1, NULL, true, false, 0, 0, 0, 0); 549 } 550 sem_post(&_job_end_wait_sem); 551 } 552 break; 553 554 case PRINT_STATUS_CANCELLED: 555 sem_post(&_job_start_wait_sem); 556 if ((jq->print_ifc != NULL) && (jq->print_ifc->enable_timeout != NULL)) { 557 jq->print_ifc->enable_timeout(jq->print_ifc, 1); 558 } 559 if (statusold != PRINT_STATUS_CANCELLED) { 560 LOGI("status requested job cancel"); 561 if (new_status->printer_reasons[0] == PRINT_STATUS_OFFLINE) { 562 sem_post(&_job_start_wait_sem); 563 sem_post(&_job_end_wait_sem); 564 if ((jq->print_ifc != NULL) && (jq->print_ifc->enable_timeout != NULL)) { 565 jq->print_ifc->enable_timeout(jq->print_ifc, 1); 566 } 567 } 568 _lock(); 569 jq->job_params.cancelled = true; 570 _unlock(); 571 } 572 if (new_status->printer_reasons[0] == PRINT_STATUS_OFFLINE) { 573 sem_post(&_job_start_wait_sem); 574 sem_post(&_job_end_wait_sem); 575 } 576 break; 577 578 case PRINT_STATUS_PRINTING: 579 sem_post(&_job_start_wait_sem); 580 _lock(); 581 if ((jq->job_state != JOB_STATE_RUNNING) || (jq->blocked_reasons != blocked_reasons)) { 582 jq->job_state = JOB_STATE_RUNNING; 583 jq->blocked_reasons = blocked_reasons; 584 if (jq->cb_fn) { 585 cb_param.state = JOB_RUNNING; 586 cb_param.blocked_reasons = jq->blocked_reasons; 587 cb_param.job_done_result = OK; 588 589 jq->cb_fn(jq->job_handle, (void *) &cb_param); 590 } 591 } 592 _unlock(); 593 break; 594 595 case PRINT_STATUS_UNABLE_TO_CONNECT: 596 sem_post(&_job_start_wait_sem); 597 _lock(); 598 _stop_status_thread(jq); 599 600 jq->blocked_reasons = blocked_reasons; 601 jq->job_params.cancelled = true; 602 jq->job_state = JOB_STATE_ERROR; 603 if (jq->cb_fn) { 604 cb_param.state = JOB_DONE; 605 cb_param.blocked_reasons = blocked_reasons; 606 cb_param.job_done_result = ERROR; 607 608 jq->cb_fn(jq->job_handle, (void *) &cb_param); 609 } 610 611 if (jq->print_ifc != NULL) { 612 jq->print_ifc->destroy(jq->print_ifc); 613 jq->print_ifc = NULL; 614 } 615 616 if (jq->status_ifc != NULL) { 617 jq->status_ifc->destroy(jq->status_ifc); 618 jq->status_ifc = NULL; 619 } 620 621 _unlock(); 622 sem_post(&_job_end_wait_sem); 623 break; 624 625 default: 626 // an error has occurred, report it back to the client 627 sem_post(&_job_start_wait_sem); 628 _lock(); 629 630 if ((jq->job_state != JOB_STATE_BLOCKED) || (jq->blocked_reasons != blocked_reasons)) { 631 jq->job_state = JOB_STATE_BLOCKED; 632 jq->blocked_reasons = blocked_reasons; 633 if (jq->cb_fn) { 634 cb_param.state = JOB_BLOCKED; 635 cb_param.blocked_reasons = blocked_reasons; 636 cb_param.job_done_result = OK; 637 638 jq->cb_fn(jq->job_handle, (void *) &cb_param); 639 } 640 } 641 _unlock(); 642 break; 643 } 644 } 645 646 static void *_job_status_thread(void *param) { 647 _job_queue_t *jq = (_job_queue_t *) param; 648 (jq->status_ifc->start)(jq->status_ifc, _job_status_callback, param); 649 return NULL; 650 } 651 652 static int _start_status_thread(_job_queue_t *jq) { 653 sigset_t allsig, oldsig; 654 int result = ERROR; 655 656 if ((jq == NULL) || (jq->status_ifc == NULL)) { 657 return result; 658 } 659 660 result = OK; 661 sigfillset(&allsig); 662 #if CHECK_PTHREAD_SIGMASK_STATUS 663 result = pthread_sigmask(SIG_SETMASK, &allsig, &oldsig); 664 #else // else CHECK_PTHREAD_SIGMASK_STATUS 665 pthread_sigmask(SIG_SETMASK, &allsig, &oldsig); 666 #endif // CHECK_PTHREAD_SIGMASK_STATUS 667 if (result == OK) { 668 result = pthread_create(&_job_status_tid, 0, _job_status_thread, jq); 669 if ((result == ERROR) && (_job_status_tid != pthread_self())) { 670 #if USE_PTHREAD_CANCEL 671 pthread_cancel(_job_status_tid); 672 #else // else USE_PTHREAD_CANCEL 673 pthread_kill(_job_status_tid, SIGKILL); 674 #endif // USE_PTHREAD_CANCEL 675 _job_status_tid = pthread_self(); 676 } 677 } 678 679 if (result == OK) { 680 sched_yield(); 681 #if CHECK_PTHREAD_SIGMASK_STATUS 682 result = pthread_sigmask(SIG_SETMASK, &oldsig, 0); 683 #else // else CHECK_PTHREAD_SIGMASK_STATUS 684 pthread_sigmask(SIG_SETMASK, &oldsig, 0); 685 #endif // CHECK_PTHREAD_SIGMASK_STATUS 686 } 687 return result; 688 } 689 690 /* 691 * Runs a print job. Contains logic for what to do given different printer statuses. 692 */ 693 static void *_job_thread(void *param) { 694 wprint_job_callback_params_t cb_param; 695 _msg_t msg; 696 wJob_t job_handle; 697 _job_queue_t *jq; 698 _page_t page; 699 int i; 700 status_t job_result; 701 int corrupted = 0; 702 703 while (OK == msgQReceive(_msgQ, (char *) &msg, sizeof(msg), WAIT_FOREVER)) { 704 if (msg.id == MSG_RUN_JOB) { 705 LOGI("_job_thread(): Received message: MSG_RUN_JOB"); 706 } else { 707 LOGI("_job_thread(): Received message: MSG_QUIT"); 708 } 709 710 if (msg.id == MSG_QUIT) { 711 break; 712 } 713 714 job_handle = msg.job_id; 715 716 // check if this is a valid job_handle that is still active 717 _lock(); 718 719 jq = _get_job_desc(job_handle); 720 721 // set state to running and invoke the plugin, there is one 722 if (jq) { 723 if (jq->job_state != JOB_STATE_QUEUED) { 724 _unlock(); 725 continue; 726 } 727 corrupted = 0; 728 job_result = OK; 729 jq->job_params.plugin_data = NULL; 730 731 // clear out the semaphore just in case 732 while (sem_trywait(&_job_start_wait_sem) == OK) { 733 } 734 while (sem_trywait(&_job_end_wait_sem) == OK) { 735 } 736 737 // initialize the status ifc 738 if (jq->status_ifc != NULL) { 739 wprint_connect_info_t connect_info; 740 connect_info.printer_addr = jq->printer_addr; 741 connect_info.uri_path = jq->printer_uri; 742 connect_info.port_num = jq->port_num; 743 connect_info.uri_scheme = IPP_PREFIX; 744 jq->status_ifc->init(jq->status_ifc, &connect_info); 745 } 746 // wait for the printer to be idle 747 if ((jq->status_ifc != NULL) && (jq->status_ifc->get_status != NULL)) { 748 int retry = 0; 749 int loop = 1; 750 printer_state_dyn_t printer_state; 751 do { 752 print_status_t status; 753 jq->status_ifc->get_status(jq->status_ifc, &printer_state); 754 status = printer_state.printer_status & ~PRINTER_IDLE_BIT; 755 756 switch (status) { 757 case PRINT_STATUS_IDLE: 758 printer_state.printer_status = PRINT_STATUS_IDLE; 759 jq->blocked_reasons = 0; 760 loop = 0; 761 break; 762 case PRINT_STATUS_UNKNOWN: 763 if (printer_state.printer_reasons[0] == PRINT_STATUS_UNKNOWN) { 764 LOGE("PRINTER STATUS UNKNOWN - Ln 747 libwprint.c"); 765 // no status available, break out and hope for the best 766 printer_state.printer_status = PRINT_STATUS_IDLE; 767 loop = 0; 768 break; 769 } 770 case PRINT_STATUS_SVC_REQUEST: 771 if ((printer_state.printer_reasons[0] == PRINT_STATUS_UNABLE_TO_CONNECT) 772 || (printer_state.printer_reasons[0] == PRINT_STATUS_OFFLINE)) { 773 LOGD("_job_thread: Received an Unable to Connect message"); 774 jq->blocked_reasons = BLOCKED_REASON_UNABLE_TO_CONNECT; 775 loop = 0; 776 break; 777 } 778 default: 779 if (printer_state.printer_status & PRINTER_IDLE_BIT) { 780 LOGD("printer blocked but appears to be in an idle state. " 781 "Allowing job to proceed"); 782 printer_state.printer_status = PRINT_STATUS_IDLE; 783 loop = 0; 784 break; 785 } else if (retry >= MAX_IDLE_WAIT) { 786 jq->blocked_reasons |= BLOCKED_REASONS_PRINTER_BUSY; 787 loop = 0; 788 } else if (!jq->job_params.cancelled) { 789 int blocked_reasons = 0; 790 for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) { 791 if (printer_state.printer_reasons[i] == 792 PRINT_STATUS_MAX_STATE) { 793 break; 794 } 795 blocked_reasons |= (1 << printer_state.printer_reasons[i]); 796 } 797 if (blocked_reasons == 0) { 798 blocked_reasons |= BLOCKED_REASONS_PRINTER_BUSY; 799 } 800 801 if ((jq->job_state != JOB_STATE_BLOCKED) || 802 (jq->blocked_reasons != blocked_reasons)) { 803 jq->job_state = JOB_STATE_BLOCKED; 804 jq->blocked_reasons = blocked_reasons; 805 if (jq->cb_fn) { 806 cb_param.state = JOB_BLOCKED; 807 cb_param.blocked_reasons = blocked_reasons; 808 cb_param.job_done_result = OK; 809 810 jq->cb_fn(jq->job_handle, (void *) &cb_param); 811 } 812 } 813 _unlock(); 814 sleep(1); 815 _lock(); 816 retry++; 817 } 818 break; 819 } 820 if (jq->job_params.cancelled) { 821 loop = 0; 822 } 823 } while (loop); 824 825 if (jq->job_params.cancelled) { 826 job_result = CANCELLED; 827 } else { 828 job_result = (((printer_state.printer_status & ~PRINTER_IDLE_BIT) == 829 PRINT_STATUS_IDLE) ? OK : ERROR); 830 } 831 } 832 833 _job_status_tid = pthread_self(); 834 if (job_result == OK) { 835 if (jq->print_ifc) { 836 job_result = jq->print_ifc->init(jq->print_ifc, jq->printer_addr, 837 jq->port_num, jq->printer_uri); 838 if (job_result == ERROR) { 839 jq->blocked_reasons = BLOCKED_REASON_UNABLE_TO_CONNECT; 840 } 841 } 842 } 843 if (job_result == OK) { 844 _start_status_thread(jq); 845 } 846 847 /* call the plugin's start_job method, if no other job is running 848 use callback to notify the client */ 849 850 if ((job_result == OK) && jq->cb_fn) { 851 cb_param.state = JOB_RUNNING; 852 cb_param.blocked_reasons = 0; 853 cb_param.job_done_result = OK; 854 855 jq->cb_fn(job_handle, (void *) &cb_param); 856 } 857 858 jq->job_params.page_num = -1; 859 if (job_result == OK) { 860 if (jq->print_ifc != NULL) { 861 LOGD("_job_thread: Calling validate_job"); 862 if (jq->print_ifc->validate_job != NULL) { 863 job_result = jq->print_ifc->validate_job(jq->print_ifc, &jq->job_params); 864 } 865 866 /* PDF format plugin's start_job and end_job are to be called for each copy, 867 * inside the for-loop for num_copies. 868 */ 869 870 // Do not call start_job unless validate_job returned OK 871 if ((job_result == OK) && (jq->print_ifc->start_job != NULL) && 872 (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) != 0)) { 873 jq->print_ifc->start_job(jq->print_ifc, &jq->job_params); 874 } 875 } 876 877 // Do not call start_job unless validate_job returned OK 878 if (job_result == OK && jq->plugin->start_job != NULL) { 879 job_result = jq->plugin->start_job(job_handle, (void *) &_wprint_ifc, 880 (void *) jq->print_ifc, &(jq->job_params)); 881 } 882 } 883 884 if (job_result == OK) { 885 jq->job_params.page_num = 0; 886 } 887 888 // multi-page print job 889 if (jq->is_dir && (job_result == OK)) { 890 int per_copy_page_num; 891 for (i = 0; (i < jq->job_params.num_copies) && 892 ((job_result == OK) || (job_result == CORRUPT)) && 893 (!jq->job_params.cancelled); i++) { 894 if ((i > 0) && 895 jq->job_params.copies_supported && 896 (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) == 0)) { 897 LOGD("_job_thread multi_page: breaking out copies supported"); 898 break; 899 } 900 bool pdf_printed = false; 901 if (jq->print_ifc->start_job != NULL && 902 (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) == 0)) { 903 jq->print_ifc->start_job(jq->print_ifc, &jq->job_params); 904 } 905 906 per_copy_page_num = 0; 907 jq->job_state = JOB_STATE_RUNNING; 908 909 // while there is a page to print 910 _unlock(); 911 912 while (OK == msgQReceive(jq->pageQ, (char *) &page, sizeof(page), 913 WAIT_FOREVER)) { 914 _lock(); 915 916 // check for any printing problems so far 917 if (jq->print_ifc->check_status) { 918 if (jq->print_ifc->check_status(jq->print_ifc) == ERROR) { 919 job_result = ERROR; 920 break; 921 } 922 } 923 924 /* take empty filename as cue to break out of the loop 925 * but we have to do last_page processing 926 */ 927 928 // all copies are clubbed together as a single print job 929 if (page.last_page && ((i == jq->job_params.num_copies - 1) || 930 (jq->job_params.copies_supported && 931 strcmp(jq->job_params.print_format, 932 PRINT_FORMAT_PDF) == 0))) { 933 jq->job_params.last_page = page.last_page; 934 } else { 935 jq->job_params.last_page = false; 936 } 937 938 if (strlen(page.filename) > 0) { 939 per_copy_page_num++; 940 { 941 jq->job_params.page_num++; 942 } 943 if (page.pdf_page) { 944 jq->job_params.page_num = page.page_num; 945 } else { 946 jq->job_params.page_num = per_copy_page_num; 947 } 948 949 // setup page margin information 950 jq->job_params.print_top_margin += page.top_margin; 951 jq->job_params.print_left_margin += page.left_margin; 952 jq->job_params.print_right_margin += page.right_margin; 953 jq->job_params.print_bottom_margin += page.bottom_margin; 954 955 jq->job_params.copy_num = (i + 1); 956 jq->job_params.copy_page_num = page.page_num; 957 jq->job_params.page_backside = (per_copy_page_num & 0x1); 958 jq->job_params.page_corrupted = (page.corrupted ? 1 : 0); 959 jq->job_params.page_printing = true; 960 _unlock(); 961 962 if (!page.corrupted) { 963 LOGD("_job_thread(): page not corrupt, calling plugin's print_page" 964 " function for page #%d", page.page_num); 965 if (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) != 0) { 966 job_result = jq->plugin->print_page(&(jq->job_params), 967 jq->mime_type, 968 page.filename); 969 } else if (!pdf_printed) { 970 // for PDF plugin, print_page prints entire document, 971 // so need to be called only once 972 job_result = jq->plugin->print_page(&(jq->job_params), 973 jq->mime_type, 974 page.filename); 975 pdf_printed = true; 976 } 977 } else { 978 LOGD("_job_thread(): page IS corrupt, printing blank page for " 979 "page #%d", page.page_num); 980 job_result = CORRUPT; 981 if ((jq->job_params.duplex != DUPLEX_MODE_NONE) && 982 (jq->plugin->print_blank_page != NULL)) { 983 jq->plugin->print_blank_page(job_handle, &(jq->job_params)); 984 } 985 } 986 _lock(); 987 988 jq->job_params.print_top_margin -= page.top_margin; 989 jq->job_params.print_left_margin -= page.left_margin; 990 jq->job_params.print_right_margin -= page.right_margin; 991 jq->job_params.print_bottom_margin -= page.bottom_margin; 992 jq->job_params.page_printing = false; 993 994 // make sure we only count corrupted pages once 995 if (page.corrupted == false) { 996 page.corrupted = ((job_result == CORRUPT) ? true : false); 997 corrupted += (job_result == CORRUPT); 998 } 999 } 1000 1001 // make sure we always print an even number of pages in duplex jobs 1002 if (page.last_page && (jq->job_params.duplex != DUPLEX_MODE_NONE) 1003 && (jq->job_params.page_backside) 1004 && (jq->plugin->print_blank_page != NULL)) { 1005 _unlock(); 1006 jq->plugin->print_blank_page(job_handle, &(jq->job_params)); 1007 _lock(); 1008 } 1009 1010 // if multiple copies are requested, save the contents of the pageQ message 1011 if (jq->saveQ && !jq->job_params.cancelled && (job_result != ERROR)) { 1012 job_result = msgQSend(jq->saveQ, (char *) &page, 1013 sizeof(page), NO_WAIT, MSG_Q_FIFO); 1014 1015 // swap pageQ and saveQ 1016 if (page.last_page && !jq->job_params.last_page) { 1017 msg_q_id tmpQ = jq->pageQ; 1018 jq->pageQ = jq->saveQ; 1019 jq->saveQ = tmpQ; 1020 1021 // defensive programming 1022 while (msgQNumMsgs(tmpQ) > 0) { 1023 msgQReceive(tmpQ, (char *) &page, sizeof(page), NO_WAIT); 1024 LOGE("pageQ inconsistencies, discarding page #%d, file %s", 1025 page.page_num, page.filename); 1026 } 1027 } 1028 } 1029 1030 if (page.last_page || jq->job_params.cancelled) { 1031 // Leave the sempahore locked 1032 break; 1033 } 1034 1035 // unlock to go back to the top of the while loop 1036 _unlock(); 1037 } // while there is another page 1038 1039 if ((strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) == 0) && 1040 (jq->print_ifc->end_job)) { 1041 int end_job_result = jq->print_ifc->end_job(jq->print_ifc); 1042 if (job_result == OK) { 1043 if (end_job_result == ERROR) { 1044 job_result = ERROR; 1045 } else if (end_job_result == CANCELLED) { 1046 job_result = CANCELLED; 1047 } 1048 } 1049 } 1050 } // for each copy of the job 1051 } else if (job_result == OK) { 1052 // single page job 1053 for (i = 0; ((i < jq->job_params.num_copies) && (job_result == OK)); i++) { 1054 if ((i > 0) && jq->job_params.copies_supported && 1055 (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) == 0)) { 1056 LOGD("_job_thread single_page: breaking out copies supported"); 1057 break; 1058 } 1059 1060 // check for any printing problems so far 1061 if ((jq->print_ifc != NULL) && (jq->print_ifc->check_status)) { 1062 if (jq->print_ifc->check_status(jq->print_ifc) == ERROR) { 1063 job_result = ERROR; 1064 break; 1065 } 1066 } 1067 1068 jq->job_state = JOB_STATE_RUNNING; 1069 jq->job_params.page_num++; 1070 jq->job_params.last_page = (i == (jq->job_params.num_copies - 1)); 1071 jq->job_params.copy_num = (i + 1); 1072 jq->job_params.copy_page_num = 1; 1073 jq->job_params.page_corrupted = (job_result == CORRUPT); 1074 jq->job_params.page_printing = true; 1075 1076 _unlock(); 1077 job_result = jq->plugin->print_page(&(jq->job_params), jq->mime_type, 1078 jq->pathname); 1079 1080 if ((jq->job_params.duplex != DUPLEX_MODE_NONE) 1081 && (jq->plugin->print_blank_page != NULL)) { 1082 jq->plugin->print_blank_page(job_handle, 1083 &(jq->job_params)); 1084 } 1085 1086 _lock(); 1087 jq->job_params.page_printing = false; 1088 1089 corrupted += (job_result == CORRUPT); 1090 } // for each copy 1091 } 1092 1093 // if we started the job end it 1094 if (jq->job_params.page_num >= 0) { 1095 // if the job was cancelled without sending anything through, print a blank sheet 1096 if ((jq->job_params.page_num == 0) 1097 && (jq->plugin->print_blank_page != NULL)) { 1098 jq->plugin->print_blank_page(job_handle, &(jq->job_params)); 1099 } 1100 if (jq->plugin->end_job != NULL) { 1101 jq->plugin->end_job(&(jq->job_params)); 1102 } 1103 if ((jq->print_ifc != NULL) && (jq->print_ifc->end_job) && 1104 (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) != 0)) { 1105 int end_job_result = jq->print_ifc->end_job(jq->print_ifc); 1106 if (job_result == OK) { 1107 if (end_job_result == ERROR) { 1108 job_result = ERROR; 1109 } else if (end_job_result == CANCELLED) { 1110 job_result = CANCELLED; 1111 } 1112 } 1113 } 1114 } 1115 1116 // if we started to print, wait for idle 1117 if ((jq->job_params.page_num > 0) && (jq->status_ifc != NULL)) { 1118 int retry, result; 1119 _unlock(); 1120 1121 for (retry = 0, result = ERROR; ((result == ERROR) && (retry <= MAX_START_WAIT)); 1122 retry++) { 1123 if (retry != 0) { 1124 sleep(1); 1125 } 1126 result = sem_trywait(&_job_start_wait_sem); 1127 } 1128 1129 if (result == OK) { 1130 for (retry = 0, result = ERROR; ((result == ERROR) && (retry <= MAX_DONE_WAIT)); 1131 retry++) { 1132 if (retry != 0) { 1133 _lock(); 1134 if (jq->job_params.cancelled && !jq->cancel_ok) { 1135 /* The user tried to cancel and it either didn't go through 1136 * or the printer doesn't support cancel through an OID. 1137 * Either way it's pointless to sit here waiting for idle when 1138 * may never come, so we'll bail out early 1139 */ 1140 retry = (MAX_DONE_WAIT + 1); 1141 } 1142 _unlock(); 1143 sleep(1); 1144 if (retry == MAX_DONE_WAIT) { 1145 _lock(); 1146 if (!jq->job_params.cancelled && 1147 (jq->blocked_reasons 1148 & (BLOCKED_REASON_OUT_OF_PAPER 1149 | BLOCKED_REASON_JAMMED 1150 | BLOCKED_REASON_DOOR_OPEN))) { 1151 retry = (MAX_DONE_WAIT - 1); 1152 } 1153 _unlock(); 1154 } 1155 } 1156 result = sem_trywait(&_job_end_wait_sem); 1157 } 1158 } else { 1159 LOGD("_job_thread(): the job never started"); 1160 } 1161 _lock(); 1162 } 1163 1164 // make sure page_num doesn't stay as a negative number 1165 jq->job_params.page_num = MAX(0, jq->job_params.page_num); 1166 _stop_status_thread(jq); 1167 1168 if (corrupted != 0) { 1169 job_result = CORRUPT; 1170 } 1171 1172 LOGI("job_thread(): with job_state value: %d ", jq->job_state); 1173 if ((jq->job_state == JOB_STATE_COMPLETED) || (jq->job_state == JOB_STATE_ERROR) 1174 || (jq->job_state == JOB_STATE_CANCELLED) 1175 || (jq->job_state == JOB_STATE_CORRUPTED) 1176 || (jq->job_state == JOB_STATE_FREE)) { 1177 LOGI("_job_thread(): job finished early: do not send callback again"); 1178 } else { 1179 switch (job_result) { 1180 case OK: 1181 if (!jq->job_params.cancelled) { 1182 jq->job_state = JOB_STATE_COMPLETED; 1183 jq->blocked_reasons = 0; 1184 break; 1185 } else { 1186 job_result = CANCELLED; 1187 } 1188 case CANCELLED: 1189 jq->job_state = JOB_STATE_CANCELLED; 1190 jq->blocked_reasons = BLOCKED_REASONS_CANCELLED; 1191 if (!jq->cancel_ok) { 1192 jq->blocked_reasons |= BLOCKED_REASON_PARTIAL_CANCEL; 1193 } 1194 break; 1195 case CORRUPT: 1196 LOGE("_job_thread(): %d file(s) in the job were corrupted", corrupted); 1197 jq->job_state = JOB_STATE_CORRUPTED; 1198 jq->blocked_reasons = 0; 1199 break; 1200 case ERROR: 1201 default: 1202 LOGE("_job_thread(): ERROR plugin->start_job(%ld): %s => %s", job_handle, 1203 jq->mime_type, jq->job_params.print_format); 1204 job_result = ERROR; 1205 jq->job_state = JOB_STATE_ERROR; 1206 break; 1207 } // job_result 1208 1209 // end of job callback 1210 if (jq->cb_fn) { 1211 cb_param.state = JOB_DONE; 1212 cb_param.blocked_reasons = jq->blocked_reasons; 1213 cb_param.job_done_result = job_result; 1214 1215 jq->cb_fn(job_handle, (void *) &cb_param); 1216 } 1217 1218 if (jq->print_ifc != NULL) { 1219 jq->print_ifc->destroy(jq->print_ifc); 1220 jq->print_ifc = NULL; 1221 } 1222 1223 if (jq->status_ifc != NULL) { 1224 jq->status_ifc->destroy(jq->status_ifc); 1225 jq->status_ifc = NULL; 1226 } 1227 } 1228 } else { 1229 LOGI("_job_thread(): job %ld not in queue .. maybe cancelled", job_handle); 1230 } 1231 1232 _unlock(); 1233 LOGI("_job_thread(): job finished: %ld", job_handle); 1234 } 1235 1236 sem_post(&_job_end_wait_sem); 1237 return NULL; 1238 } 1239 1240 /* 1241 * Starts the wprint background job thread 1242 */ 1243 static int _start_thread(void) { 1244 sigset_t allsig, oldsig; 1245 int result; 1246 1247 _job_tid = pthread_self(); 1248 1249 result = OK; 1250 sigfillset(&allsig); 1251 #if CHECK_PTHREAD_SIGMASK_STATUS 1252 result = pthread_sigmask(SIG_SETMASK, &allsig, &oldsig); 1253 #else // else CHECK_PTHREAD_SIGMASK_STATUS 1254 pthread_sigmask(SIG_SETMASK, &allsig, &oldsig); 1255 #endif // CHECK_PTHREAD_SIGMASK_STATUS 1256 if (result == OK) { 1257 result = pthread_create(&_job_tid, 0, _job_thread, NULL); 1258 if ((result == ERROR) && (_job_tid != pthread_self())) { 1259 #if USE_PTHREAD_CANCEL 1260 pthread_cancel(_job_tid); 1261 #else // else USE_PTHREAD_CANCEL 1262 pthread_kill(_job_tid, SIGKILL); 1263 #endif // USE_PTHREAD_CANCEL 1264 _job_tid = pthread_self(); 1265 } 1266 } 1267 1268 if (result == OK) { 1269 sched_yield(); 1270 #if CHECK_PTHREAD_SIGMASK_STATUS 1271 result = pthread_sigmask(SIG_SETMASK, &oldsig, 0); 1272 #else // else CHECK_PTHREAD_SIGMASK_STATUS 1273 pthread_sigmask(SIG_SETMASK, &oldsig, 0); 1274 #endif // CHECK_PTHREAD_SIGMASK_STATUS 1275 } 1276 1277 return result; 1278 } 1279 1280 /* 1281 * Waits for the job thread to reach a stopped state 1282 */ 1283 static int _stop_thread(void) { 1284 if (!pthread_equal(_job_tid, pthread_self())) { 1285 pthread_join(_job_tid, 0); 1286 _job_tid = pthread_self(); 1287 return OK; 1288 } else { 1289 return ERROR; 1290 } 1291 } 1292 1293 static const wprint_io_plugin_t _file_io_plugin = { 1294 .version = WPRINT_PLUGIN_VERSION(_INTERFACE_MINOR_VERSION), 1295 .port_num = PORT_FILE, .getCapsIFC = NULL, .getStatusIFC = NULL, 1296 .getPrintIFC = _printer_file_connect,}; 1297 1298 static const wprint_io_plugin_t _ipp_io_plugin = { 1299 .version = WPRINT_PLUGIN_VERSION(_INTERFACE_MINOR_VERSION), 1300 .port_num = PORT_IPP, .getCapsIFC = ipp_status_get_capabilities_ifc, 1301 .getStatusIFC = ipp_status_get_monitor_ifc, .getPrintIFC = ipp_get_print_ifc,}; 1302 1303 static void _setup_io_plugins() { 1304 _io_plugins[0].port_num = PORT_FILE; 1305 _io_plugins[0].io_plugin = &_file_io_plugin; 1306 1307 _io_plugins[1].port_num = PORT_IPP; 1308 _io_plugins[1].io_plugin = &_ipp_io_plugin; 1309 } 1310 1311 extern wprint_plugin_t *libwprintplugin_pcl_reg(void); 1312 1313 extern wprint_plugin_t *libwprintplugin_pdf_reg(void); 1314 1315 static void _setup_print_plugins() { 1316 plugin_reset(); 1317 plugin_add(libwprintplugin_pcl_reg()); 1318 plugin_add(libwprintplugin_pdf_reg()); 1319 } 1320 1321 bool wprintIsRunning() { 1322 return _msgQ != 0; 1323 } 1324 1325 int wprintInit(void) { 1326 int count = 0; 1327 1328 _setup_print_plugins(); 1329 _setup_io_plugins(); 1330 1331 _msgQ = msgQCreate(_MAX_MSGS, sizeof(_msg_t)); 1332 1333 if (!_msgQ) { 1334 LOGE("ERROR: cannot create msgQ"); 1335 return ERROR; 1336 } 1337 1338 sem_init(&_job_end_wait_sem, 0, 0); 1339 sem_init(&_job_start_wait_sem, 0, 0); 1340 1341 signal(SIGPIPE, SIG_IGN); // avoid broken pipe process shutdowns 1342 pthread_mutexattr_settype(&_q_lock_attr, PTHREAD_MUTEX_RECURSIVE_NP); 1343 pthread_mutex_init(&_q_lock, &_q_lock_attr); 1344 1345 if (_start_thread() != OK) { 1346 LOGE("could not start job thread"); 1347 return ERROR; 1348 } 1349 return count; 1350 } 1351 1352 static const printer_capabilities_t _default_cap = {.color = true, .borderless = true, 1353 .numSupportedMediaSizes = 0, .numSupportedMediaTrays = 0, 1354 .numSupportedMediaTypes = 0,}; 1355 1356 /* 1357 * Check if a media size is supported 1358 */ 1359 static bool is_supported(media_size_t media_size) { 1360 int i; 1361 for (i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) { 1362 if (SupportedMediaSizes[i].media_size == media_size) return true; 1363 } 1364 return false; 1365 } 1366 1367 /* 1368 * Checks printers reported media sizes and validates that wprint supports them 1369 */ 1370 static void _validate_supported_media_sizes(printer_capabilities_t *printer_cap) { 1371 if (printer_cap == NULL) return; 1372 1373 if (printer_cap->numSupportedMediaSizes == 0) { 1374 unsigned int i = 0; 1375 printer_cap->supportedMediaSizes[i++] = ISO_A4; 1376 printer_cap->supportedMediaSizes[i++] = US_LETTER; 1377 printer_cap->supportedMediaSizes[i++] = INDEX_CARD_4X6; 1378 printer_cap->supportedMediaSizes[i++] = INDEX_CARD_5X7; 1379 printer_cap->numSupportedMediaSizes = i; 1380 } else { 1381 unsigned int read, write; 1382 for (read = write = 0; read < printer_cap->numSupportedMediaSizes; read++) { 1383 if (is_supported(printer_cap->supportedMediaSizes[read])) { 1384 printer_cap->supportedMediaSizes[write++] = 1385 printer_cap->supportedMediaSizes[read]; 1386 } 1387 } 1388 printer_cap->numSupportedMediaSizes = write; 1389 } 1390 } 1391 1392 /* 1393 * Checks printers numSupportedMediaTrays. If none, then add Auto. 1394 */ 1395 static void _validate_supported_media_trays(printer_capabilities_t *printer_cap) { 1396 if (printer_cap == NULL) return; 1397 1398 if (printer_cap->numSupportedMediaTrays == 0) { 1399 printer_cap->supportedMediaTrays[0] = TRAY_SRC_AUTO_SELECT; 1400 printer_cap->numSupportedMediaTrays = 1; 1401 } 1402 } 1403 1404 /* 1405 * Add a printer's supported input formats to the capabilities struct 1406 */ 1407 static void _collect_supported_input_formats(printer_capabilities_t *printer_caps) { 1408 unsigned long long input_formats = 0; 1409 plugin_get_passthru_input_formats(&input_formats); 1410 1411 // remove things the printer can't support 1412 if (!printer_caps->canPrintPDF) { 1413 input_formats &= ~(1 << INPUT_MIME_TYPE_PDF); 1414 } 1415 if (!printer_caps->canPrintPCLm) { 1416 input_formats &= ~(1 << INPUT_MIME_TYPE_PCLM); 1417 } 1418 if (!printer_caps->canPrintPWG) { 1419 input_formats &= ~(1 << INPUT_MIME_TYPE_PWG); 1420 } 1421 printer_caps->supportedInputMimeTypes = input_formats; 1422 } 1423 1424 /* 1425 * Check the print resolutions supported by the printer and verify that wprint supports them. 1426 * If nothing is found, the desired resolution is selected. 1427 */ 1428 static unsigned int _findCloseResolutionSupported(int desiredResolution, int maxResolution, 1429 const printer_capabilities_t *printer_cap) { 1430 int closeResolution = 0; 1431 int closeDifference = 0; 1432 unsigned int index = 0; 1433 for (index = 0; index < printer_cap->numSupportedResolutions; index++) { 1434 int resolution = printer_cap->supportedResolutions[index]; 1435 if (resolution == desiredResolution) { 1436 // An exact match wins.. stop looking. 1437 return resolution; 1438 } else { 1439 int difference = abs(desiredResolution - resolution); 1440 if ((closeResolution == 0) || (difference < closeDifference)) { 1441 if (resolution <= maxResolution) { 1442 // We found a better match now.. record it but keep looking. 1443 closeResolution = resolution; 1444 closeDifference = difference; 1445 } 1446 } 1447 } 1448 } 1449 1450 // If we get here we did not find an exact match. 1451 if (closeResolution == 0) { 1452 // We did not find anything.. just pick the desired value. 1453 closeResolution = desiredResolution; 1454 } 1455 return closeResolution; 1456 } 1457 1458 status_t wprintGetCapabilities(const wprint_connect_info_t *connect_info, 1459 printer_capabilities_t *printer_cap) { 1460 LOGD("wprintGetCapabilities: Enter"); 1461 status_t result = ERROR; 1462 int index; 1463 int port_num = connect_info->port_num; 1464 const ifc_printer_capabilities_t *caps_ifc = NULL; 1465 1466 memcpy(printer_cap, &_default_cap, sizeof(printer_capabilities_t)); 1467 1468 caps_ifc = _get_caps_ifc(((port_num == 0) ? PORT_FILE : PORT_IPP)); 1469 LOGD("wprintGetCapabilities: after getting caps ifc: %p", caps_ifc); 1470 switch (port_num) { 1471 case PORT_FILE: 1472 printer_cap->duplex = 1; 1473 printer_cap->borderless = 1; 1474 printer_cap->canPrintPCLm = (_default_pcl_type == PCLm); 1475 printer_cap->canPrintPWG = (_default_pcl_type == PCLPWG); 1476 printer_cap->stripHeight = STRIPE_HEIGHT; 1477 result = OK; 1478 break; 1479 default: 1480 break; 1481 } 1482 1483 if (caps_ifc != NULL) { 1484 caps_ifc->init(caps_ifc, connect_info); 1485 result = caps_ifc->get_capabilities(caps_ifc, printer_cap); 1486 caps_ifc->destroy(caps_ifc); 1487 } 1488 1489 _validate_supported_media_sizes(printer_cap); 1490 _collect_supported_input_formats(printer_cap); 1491 _validate_supported_media_trays(printer_cap); 1492 1493 printer_cap->isSupported = (printer_cap->canPrintPCLm || printer_cap->canPrintPDF || 1494 printer_cap->canPrintPWG); 1495 1496 if (result == OK) { 1497 LOGD("\tmake: %s", printer_cap->make); 1498 LOGD("\thas color: %d", printer_cap->color); 1499 LOGD("\tcan duplex: %d", printer_cap->duplex); 1500 LOGD("\tcan rotate back page: %d", printer_cap->canRotateDuplexBackPage); 1501 LOGD("\tcan print borderless: %d", printer_cap->borderless); 1502 LOGD("\tcan print pdf: %d", printer_cap->canPrintPDF); 1503 LOGD("\tcan print pclm: %d", printer_cap->canPrintPCLm); 1504 LOGD("\tcan print pwg: %d", printer_cap->canPrintPWG); 1505 LOGD("\tsource application name supported: %d", printer_cap->docSourceAppName); 1506 LOGD("\tsource application version supported: %d", printer_cap->docSourceAppVersion); 1507 LOGD("\tsource os name supported: %d", printer_cap->docSourceOsName); 1508 LOGD("\tsource os version supported: %d", printer_cap->docSourceOsVersion); 1509 LOGD("\tprinter supported: %d", printer_cap->isSupported); 1510 LOGD("\tstrip height: %d", printer_cap->stripHeight); 1511 LOGD("\tinkjet: %d", printer_cap->inkjet); 1512 LOGD("\tresolutions supported:"); 1513 for (index = 0; index < printer_cap->numSupportedResolutions; index++) { 1514 LOGD("\t (%d dpi)", printer_cap->supportedResolutions[index]); 1515 } 1516 } 1517 LOGD("wprintGetCapabilities: Exit"); 1518 return result; 1519 } 1520 1521 /* 1522 * Returns a preferred print format supported by the printer 1523 */ 1524 static char *_get_print_format(const char *mime_type, const wprint_job_params_t *job_params, 1525 const printer_capabilities_t *cap) { 1526 char *print_format = NULL; 1527 1528 errno = OK; 1529 1530 if (((strcmp(mime_type, MIME_TYPE_PDF) == 0) && cap->canPrintPDF)) { 1531 // For content type=photo and a printer that supports both PCLm and PDF, 1532 // prefer PCLm over PDF. 1533 if (job_params && (strcasecmp(job_params->docCategory, "photo") == 0) && 1534 cap->canPrintPCLm) { 1535 print_format = PRINT_FORMAT_PCLM; 1536 LOGI("_get_print_format(): print_format switched from PDF to PCLm"); 1537 } else { 1538 print_format = PRINT_FORMAT_PDF; 1539 } 1540 } else if (cap->canPrintPCLm || cap->canPrintPDF) { 1541 // PCLm is a subset of PDF 1542 print_format = PRINT_FORMAT_PCLM; 1543 #if (USE_PWG_OVER_PCLM != 0) 1544 if (cap->canPrintPWG) { 1545 print_format = PRINT_FORMAT_PWG; 1546 } 1547 #endif // (USE_PWG_OVER_PCLM != 0) 1548 } else if (cap->canPrintPWG) { 1549 print_format = PRINT_FORMAT_PWG; 1550 } else { 1551 errno = EBADRQC; 1552 } 1553 1554 if (print_format != NULL) { 1555 LOGI("\t_get_print_format(): print_format: %s", print_format); 1556 } 1557 1558 return print_format; 1559 } 1560 1561 status_t wprintGetDefaultJobParams(wprint_job_params_t *job_params) { 1562 status_t result = ERROR; 1563 static const wprint_job_params_t _default_job_params = {.print_format = _DEFAULT_PRINT_FORMAT, 1564 .pcl_type = _DEFAULT_PCL_TYPE, .media_size = US_LETTER, .media_type = MEDIA_PLAIN, 1565 .duplex = DUPLEX_MODE_NONE, .dry_time = DUPLEX_DRY_TIME_NORMAL, 1566 .color_space = COLOR_SPACE_COLOR, .media_tray = TRAY_SRC_AUTO_SELECT, 1567 .pixel_units = DEFAULT_RESOLUTION, .render_flags = 0, .num_copies =1, 1568 .borderless = false, .cancelled = false, .renderInReverseOrder = false, 1569 .ipp_1_0_supported = false, .ipp_2_0_supported = false, .epcl_ipp_supported = false, 1570 .strip_height = STRIPE_HEIGHT, .docCategory = {0}, 1571 .copies_supported = false}; 1572 1573 if (job_params == NULL) return result; 1574 1575 memcpy(job_params, &_default_job_params, sizeof(_default_job_params)); 1576 1577 return OK; 1578 } 1579 1580 status_t wprintGetFinalJobParams(wprint_job_params_t *job_params, 1581 const printer_capabilities_t *printer_cap) { 1582 int i; 1583 status_t result = ERROR; 1584 float margins[NUM_PAGE_MARGINS]; 1585 1586 if (job_params == NULL) { 1587 return result; 1588 } 1589 result = OK; 1590 1591 job_params->accepts_pclm = printer_cap->canPrintPCLm; 1592 job_params->accepts_pdf = printer_cap->canPrintPDF; 1593 job_params->media_default = printer_cap->mediaDefault; 1594 1595 if (printer_cap->ePclIppVersion == 1) { 1596 job_params->epcl_ipp_supported = true; 1597 } 1598 1599 if (printer_cap->canCopy) { 1600 job_params->copies_supported = true; 1601 } 1602 1603 if (printer_cap->ippVersionMajor == 2) { 1604 job_params->ipp_1_0_supported = true; 1605 job_params->ipp_2_0_supported = true; 1606 } else if (printer_cap->ippVersionMajor == 1) { 1607 job_params->ipp_1_0_supported = true; 1608 job_params->ipp_2_0_supported = false; 1609 } 1610 1611 if (!printer_cap->color) { 1612 job_params->color_space = COLOR_SPACE_MONO; 1613 } 1614 1615 if (printer_cap->canPrintPCLm || printer_cap->canPrintPDF) { 1616 job_params->pcl_type = PCLm; 1617 #if (USE_PWG_OVER_PCLM != 0) 1618 if ( printer_cap->canPrintPWG) { 1619 job_params->pcl_type = PCLPWG; 1620 } 1621 #endif // (USE_PWG_OVER_PCLM != 0) 1622 } else if (printer_cap->canPrintPWG) { 1623 job_params->pcl_type = PCLPWG; 1624 } 1625 1626 LOGD("wprintGetFinalJobParams: Using PCL Type %s", getPCLTypeString(job_params->pcl_type)); 1627 1628 // set strip height 1629 job_params->strip_height = printer_cap->stripHeight; 1630 1631 // make sure the number of copies is valid 1632 if (job_params->num_copies <= 0) { 1633 job_params->num_copies = 1; 1634 } 1635 1636 // confirm that the media size is supported 1637 for (i = 0; i < printer_cap->numSupportedMediaSizes; i++) { 1638 if (job_params->media_size == printer_cap->supportedMediaSizes[i]) { 1639 break; 1640 } 1641 } 1642 1643 if (i >= printer_cap->numSupportedMediaSizes) { 1644 job_params->media_size = ISO_A4; 1645 job_params->media_tray = TRAY_SRC_AUTO_SELECT; 1646 } 1647 1648 // check that we support the media tray 1649 for (i = 0; i < printer_cap->numSupportedMediaTrays; i++) { 1650 if (job_params->media_tray == printer_cap->supportedMediaTrays[i]) { 1651 break; 1652 } 1653 } 1654 1655 // media tray not supported, default to automatic 1656 if (i >= printer_cap->numSupportedMediaTrays) { 1657 job_params->media_tray = TRAY_SRC_AUTO_SELECT; 1658 } 1659 1660 if (printer_cap->isMediaSizeNameSupported == true) { 1661 job_params->media_size_name = true; 1662 } else { 1663 job_params->media_size_name = false; 1664 } 1665 1666 // verify borderless setting 1667 if ((job_params->borderless == true) && !printer_cap->borderless) { 1668 job_params->borderless = false; 1669 } 1670 1671 // borderless and margins don't get along 1672 if (job_params->borderless && 1673 ((job_params->job_top_margin > 0.0f) || (job_params->job_left_margin > 0.0f) || 1674 (job_params->job_right_margin > 0.0f) 1675 || (job_params->job_bottom_margin > 0.0f))) { 1676 job_params->borderless = false; 1677 } 1678 1679 // verify duplex setting 1680 if ((job_params->duplex != DUPLEX_MODE_NONE) && !printer_cap->duplex) { 1681 job_params->duplex = DUPLEX_MODE_NONE; 1682 } 1683 1684 // borderless and duplex don't get along either 1685 if (job_params->borderless && (job_params->duplex != DUPLEX_MODE_NONE)) { 1686 job_params->duplex = DUPLEX_MODE_NONE; 1687 } 1688 1689 if ((job_params->duplex == DUPLEX_MODE_BOOK) 1690 && !printer_cap->canRotateDuplexBackPage) { 1691 job_params->render_flags |= RENDER_FLAG_ROTATE_BACK_PAGE; 1692 } 1693 1694 if (job_params->render_flags & RENDER_FLAG_ROTATE_BACK_PAGE) { 1695 LOGD("wprintGetFinalJobParams: Duplex is on and device needs back page rotated."); 1696 } 1697 1698 if ((job_params->duplex == DUPLEX_MODE_NONE) && !printer_cap->faceDownTray) { 1699 job_params->renderInReverseOrder = true; 1700 } else { 1701 job_params->renderInReverseOrder = false; 1702 } 1703 1704 if (job_params->render_flags & RENDER_FLAG_AUTO_SCALE) { 1705 job_params->render_flags |= AUTO_SCALE_RENDER_FLAGS; 1706 } else if (job_params->render_flags & RENDER_FLAG_AUTO_FIT) { 1707 job_params->render_flags |= AUTO_FIT_RENDER_FLAGS; 1708 } 1709 1710 job_params->pixel_units = _findCloseResolutionSupported(DEFAULT_RESOLUTION, 1711 MAX_SUPPORTED_RESOLUTION, printer_cap); 1712 1713 printable_area_get_default_margins(job_params, printer_cap, &margins[TOP_MARGIN], 1714 &margins[LEFT_MARGIN], &margins[RIGHT_MARGIN], &margins[BOTTOM_MARGIN]); 1715 printable_area_get(job_params, margins[TOP_MARGIN], margins[LEFT_MARGIN], margins[RIGHT_MARGIN], 1716 margins[BOTTOM_MARGIN]); 1717 1718 job_params->accepts_app_name = printer_cap->docSourceAppName; 1719 job_params->accepts_app_version = printer_cap->docSourceAppVersion; 1720 job_params->accepts_os_name = printer_cap->docSourceOsName; 1721 job_params->accepts_os_version = printer_cap->docSourceOsVersion; 1722 1723 return result; 1724 } 1725 1726 wJob_t wprintStartJob(const char *printer_addr, port_t port_num, 1727 const wprint_job_params_t *job_params, const printer_capabilities_t *printer_cap, 1728 const char *mime_type, const char *pathname, wprint_status_cb_t cb_fn, 1729 const char *debugDir) { 1730 wJob_t job_handle = WPRINT_BAD_JOB_HANDLE; 1731 _msg_t msg; 1732 struct stat stat_buf; 1733 bool is_dir = false; 1734 _job_queue_t *jq; 1735 wprint_plugin_t *plugin = NULL; 1736 char *print_format; 1737 ifc_print_job_t *print_ifc; 1738 1739 if (mime_type == NULL) { 1740 errno = EINVAL; 1741 return job_handle; 1742 } 1743 1744 print_format = _get_print_format(mime_type, job_params, printer_cap); 1745 if (print_format == NULL) return job_handle; 1746 1747 // check to see if we have an appropriate plugin 1748 if (OK == stat(pathname, &stat_buf)) { 1749 if (S_ISDIR(stat_buf.st_mode)) { 1750 is_dir = true; 1751 } else if (stat_buf.st_size == 0) { 1752 errno = EBADF; 1753 return job_handle; 1754 } 1755 } else { 1756 errno = ENOENT; 1757 return job_handle; 1758 } 1759 1760 // Make sure we have job_params 1761 if (job_params == NULL) { 1762 errno = ECOMM; 1763 return job_handle; 1764 } 1765 1766 plugin = plugin_search(mime_type, print_format); 1767 _lock(); 1768 1769 if (plugin) { 1770 job_handle = _get_handle(); 1771 if (job_handle == WPRINT_BAD_JOB_HANDLE) { 1772 errno = EAGAIN; 1773 } 1774 } else { 1775 errno = ENOSYS; 1776 LOGE("wprintStartJob(): ERROR: no plugin found for %s => %s", mime_type, print_format); 1777 } 1778 1779 if (job_handle != WPRINT_BAD_JOB_HANDLE) { 1780 print_ifc = (ifc_print_job_t *) _get_print_ifc(((port_num == 0) ? PORT_FILE : PORT_IPP)); 1781 1782 // fill out the job queue record 1783 jq = _get_job_desc(job_handle); 1784 if (jq == NULL) { 1785 _recycle_handle(job_handle); 1786 job_handle = WPRINT_BAD_JOB_HANDLE; 1787 _unlock(); 1788 return job_handle; 1789 } 1790 1791 if (debugDir != NULL) { 1792 strncpy(jq->debug_path, debugDir, MAX_PATHNAME_LENGTH); 1793 jq->debug_path[MAX_PATHNAME_LENGTH] = 0; 1794 } 1795 1796 strncpy(jq->printer_addr, printer_addr, MAX_PRINTER_ADDR_LENGTH); 1797 strncpy(jq->mime_type, mime_type, MAX_MIME_LENGTH); 1798 strncpy(jq->pathname, pathname, MAX_PATHNAME_LENGTH); 1799 1800 jq->port_num = port_num; 1801 jq->cb_fn = cb_fn; 1802 jq->print_ifc = print_ifc; 1803 jq->cancel_ok = true; // assume cancel is ok 1804 jq->plugin = plugin; 1805 memcpy(jq->printer_uri, printer_cap->httpResource, 1806 MIN(ARRAY_SIZE(printer_cap->httpResource), ARRAY_SIZE(jq->printer_uri))); 1807 1808 jq->status_ifc = _get_status_ifc(((port_num == 0) ? PORT_FILE : PORT_IPP)); 1809 1810 memcpy((char *) &(jq->job_params), job_params, sizeof(wprint_job_params_t)); 1811 1812 size_t useragent_len = strlen(USERAGENT_PREFIX) + strlen(jq->job_params.docCategory) + 1; 1813 char *useragent = (char *) malloc(useragent_len); 1814 if (useragent != NULL) { 1815 snprintf(useragent, useragent_len, USERAGENT_PREFIX "%s", jq->job_params.docCategory); 1816 jq->job_params.useragent = useragent; 1817 } 1818 1819 jq->job_params.page_num = 0; 1820 jq->job_params.print_format = print_format; 1821 if (strcmp(print_format, PRINT_FORMAT_PCLM) == 0) { 1822 if (printer_cap->canPrintPCLm || printer_cap->canPrintPDF) { 1823 jq->job_params.pcl_type = PCLm; 1824 } else { 1825 jq->job_params.pcl_type = PCLNONE; 1826 } 1827 } 1828 1829 if (strcmp(print_format, PRINT_FORMAT_PWG) == 0) { 1830 if (printer_cap->canPrintPWG) { 1831 jq->job_params.pcl_type = PCLPWG; 1832 } else { 1833 jq->job_params.pcl_type = PCLNONE; 1834 } 1835 } 1836 1837 // if the pathname is a directory, then this is a multi-page job with individual pages 1838 if (is_dir) { 1839 jq->is_dir = true; 1840 jq->num_pages = 0; 1841 1842 // create a pageQ for queuing page information 1843 jq->pageQ = msgQCreate(_MAX_PAGES_PER_JOB, sizeof(_page_t)); 1844 1845 // create a secondary page Q for subsequently saving page data for copies #2 to n 1846 if (jq->job_params.num_copies > 1) { 1847 jq->saveQ = msgQCreate(_MAX_PAGES_PER_JOB, sizeof(_page_t)); 1848 } 1849 } else { 1850 jq->num_pages = 1; 1851 } 1852 1853 // post a message with job_handle to the msgQ that is serviced by a thread 1854 msg.id = MSG_RUN_JOB; 1855 msg.job_id = job_handle; 1856 1857 if (print_ifc && plugin && plugin->print_page && 1858 (msgQSend(_msgQ, (char *) &msg, sizeof(msg), NO_WAIT, MSG_Q_FIFO) == OK)) { 1859 errno = OK; 1860 LOGD("wprintStartJob(): print job %ld queued (%s => %s)", job_handle, 1861 mime_type, print_format); 1862 } else { 1863 if (print_ifc == NULL) { 1864 errno = EAFNOSUPPORT; 1865 } else if ((plugin == NULL) || (plugin->print_page == NULL)) { 1866 errno = ELIBACC; 1867 } else { 1868 errno = EBADMSG; 1869 } 1870 1871 LOGE("wprintStartJob(): ERROR plugin->start_job(%ld) : %s => %s", job_handle, 1872 mime_type, print_format); 1873 jq->job_state = JOB_STATE_ERROR; 1874 _recycle_handle(job_handle); 1875 job_handle = WPRINT_BAD_JOB_HANDLE; 1876 } 1877 } 1878 _unlock(); 1879 return job_handle; 1880 } 1881 1882 status_t wprintEndJob(wJob_t job_handle) { 1883 _page_t page; 1884 _job_queue_t *jq; 1885 status_t result = ERROR; 1886 1887 _lock(); 1888 jq = _get_job_desc(job_handle); 1889 1890 if (jq) { 1891 // if the job is done and is to be freed, do it 1892 if ((jq->job_state == JOB_STATE_CANCELLED) || (jq->job_state == JOB_STATE_ERROR) || 1893 (jq->job_state == JOB_STATE_CORRUPTED) || (jq->job_state == JOB_STATE_COMPLETED)) { 1894 result = OK; 1895 if (jq->pageQ) { 1896 while ((msgQNumMsgs(jq->pageQ) > 0) 1897 && (msgQReceive(jq->pageQ, (char *) &page, sizeof(page), 1898 WAIT_FOREVER) == OK)) { 1899 } 1900 result |= msgQDelete(jq->pageQ); 1901 jq->pageQ = NULL; 1902 } 1903 1904 if (jq->saveQ) { 1905 while ((msgQNumMsgs(jq->saveQ) > 0) 1906 && (msgQReceive(jq->saveQ, (char *) &page, sizeof(page), 1907 WAIT_FOREVER) == OK)) { 1908 } 1909 result |= msgQDelete(jq->saveQ); 1910 jq->saveQ = NULL; 1911 } 1912 _recycle_handle(job_handle); 1913 } else { 1914 LOGE("job %ld cannot be ended from state %d", job_handle, jq->job_state); 1915 } 1916 } else { 1917 LOGE("ERROR: wprintEndJob(%ld), job not found", job_handle); 1918 } 1919 1920 _unlock(); 1921 return result; 1922 } 1923 1924 status_t wprintPage(wJob_t job_handle, int page_num, const char *filename, bool last_page, 1925 bool pdf_page, unsigned int top_margin, unsigned int left_margin, unsigned int right_margin, 1926 unsigned int bottom_margin) { 1927 _job_queue_t *jq; 1928 _page_t page; 1929 status_t result = ERROR; 1930 struct stat stat_buf; 1931 1932 _lock(); 1933 jq = _get_job_desc(job_handle); 1934 1935 // use empty string to indicate EOJ for an empty job 1936 if (!filename) { 1937 filename = ""; 1938 last_page = true; 1939 } else if (OK == stat(filename, &stat_buf)) { 1940 if (!S_ISREG(stat_buf.st_mode) || (stat_buf.st_size == 0)) { 1941 _unlock(); 1942 return result; 1943 } 1944 } else { 1945 _unlock(); 1946 return result; 1947 } 1948 1949 // must be setup as a multi-page job, page_num must be valid, and filename must fit 1950 if (jq && jq->is_dir && !(jq->last_page_seen) && (((strlen(filename) < MAX_PATHNAME_LENGTH)) || 1951 (jq && (strcmp(filename, "") == 0) && last_page))) { 1952 memset(&page, 0, sizeof(page)); 1953 page.page_num = page_num; 1954 page.corrupted = false; 1955 page.pdf_page = pdf_page; 1956 page.last_page = last_page; 1957 page.top_margin = top_margin; 1958 page.left_margin = left_margin; 1959 page.right_margin = right_margin; 1960 page.bottom_margin = bottom_margin; 1961 1962 if ((strlen(filename) == 0) || strchr(filename, '/')) { 1963 // assume empty or complete pathname and use it as it is 1964 strncpy(page.filename, filename, MAX_PATHNAME_LENGTH); 1965 } else { 1966 // generate a complete pathname 1967 snprintf(page.filename, MAX_PATHNAME_LENGTH, "%s/%s", jq->pathname, filename); 1968 } 1969 1970 if (last_page) { 1971 jq->last_page_seen = true; 1972 } 1973 1974 result = msgQSend(jq->pageQ, (char *) &page, sizeof(page), NO_WAIT, MSG_Q_FIFO); 1975 } 1976 1977 if (result == OK) { 1978 LOGD("wprintPage(%ld, %d, %s, %d)", job_handle, page_num, filename, last_page); 1979 if (!(last_page && (strcmp(filename, "") == 0))) { 1980 jq->num_pages++; 1981 } 1982 } else { 1983 LOGE("wprintPage(%ld, %d, %s, %d)", job_handle, page_num, filename, last_page); 1984 } 1985 1986 _unlock(); 1987 return result; 1988 } 1989 1990 status_t wprintCancelJob(wJob_t job_handle) { 1991 _job_queue_t *jq; 1992 status_t result; 1993 1994 _lock(); 1995 1996 jq = _get_job_desc(job_handle); 1997 1998 if (jq) { 1999 LOGI("received cancel request"); 2000 // send a dummy page in case we're waiting on the msgQ page receive 2001 if ((jq->job_state == JOB_STATE_RUNNING) || (jq->job_state == JOB_STATE_BLOCKED)) { 2002 bool enableTimeout = true; 2003 jq->cancel_ok = true; 2004 jq->job_params.cancelled = true; 2005 wprintPage(job_handle, jq->num_pages + 1, NULL, true, false, 0, 0, 0, 0); 2006 if (jq->status_ifc) { 2007 // are we blocked waiting for the job to start 2008 if ((jq->job_state != JOB_STATE_BLOCKED) || (jq->job_params.page_num != 0)) { 2009 errno = OK; 2010 jq->cancel_ok = ((jq->status_ifc->cancel)(jq->status_ifc, 2011 jq->job_params.job_originating_user_name) == 0); 2012 if ((jq->cancel_ok == true) && (errno != OK)) { 2013 enableTimeout = false; 2014 } 2015 } 2016 } 2017 if (!jq->cancel_ok) { 2018 LOGE("CANCEL did not go through or is not supported for this device"); 2019 enableTimeout = true; 2020 } 2021 if (enableTimeout && (jq->print_ifc != NULL) && 2022 (jq->print_ifc->enable_timeout != NULL)) { 2023 jq->print_ifc->enable_timeout(jq->print_ifc, 1); 2024 } 2025 2026 errno = (jq->cancel_ok ? OK : ENOTSUP); 2027 jq->job_state = JOB_STATE_CANCEL_REQUEST; 2028 result = OK; 2029 } else if ((jq->job_state == JOB_STATE_CANCEL_REQUEST) || 2030 (jq->job_state == JOB_STATE_CANCELLED)) { 2031 result = OK; 2032 errno = (jq->cancel_ok ? OK : ENOTSUP); 2033 } else if (jq->job_state == JOB_STATE_QUEUED) { 2034 jq->job_params.cancelled = true; 2035 jq->job_state = JOB_STATE_CANCELLED; 2036 2037 if (jq->cb_fn) { 2038 wprint_job_callback_params_t cb_param; 2039 cb_param.state = JOB_DONE; 2040 cb_param.blocked_reasons = BLOCKED_REASONS_CANCELLED; 2041 cb_param.job_done_result = CANCELLED; 2042 2043 jq->cb_fn(job_handle, (void *) &cb_param); 2044 } 2045 2046 errno = OK; 2047 result = OK; 2048 } else { 2049 LOGE("job in other state"); 2050 result = ERROR; 2051 errno = EBADRQC; 2052 } 2053 } else { 2054 LOGE("could not find job"); 2055 result = ERROR; 2056 errno = EBADR; 2057 } 2058 2059 _unlock(); 2060 2061 return result; 2062 } 2063 2064 status_t wprintExit(void) { 2065 _msg_t msg; 2066 2067 if (_msgQ) { 2068 // toss the remaining messages in the msgQ 2069 while ((msgQNumMsgs(_msgQ) > 0) && 2070 (OK == msgQReceive(_msgQ, (char *) &msg, sizeof(msg), NO_WAIT))) {} 2071 2072 // send a quit message 2073 msg.id = MSG_QUIT; 2074 msgQSend(_msgQ, (char *) &msg, sizeof(msg), NO_WAIT, MSG_Q_FIFO); 2075 2076 // stop the job thread 2077 _stop_thread(); 2078 2079 // empty out the semaphore 2080 while (sem_trywait(&_job_end_wait_sem) == OK); 2081 while (sem_trywait(&_job_start_wait_sem) == OK); 2082 2083 // receive any messages just in case 2084 while ((msgQNumMsgs(_msgQ) > 0) 2085 && (OK == msgQReceive(_msgQ, (char *) &msg, sizeof(msg), NO_WAIT))) {} 2086 2087 // delete the msgQ 2088 msgQDelete(_msgQ); 2089 _msgQ = NULL; 2090 2091 sem_destroy(&_job_end_wait_sem); 2092 sem_destroy(&_job_start_wait_sem); 2093 pthread_mutex_destroy(&_q_lock); 2094 } 2095 2096 return OK; 2097 } 2098 2099 void wprintSetSourceInfo(const char *appName, const char *appVersion, const char *osName) { 2100 if (appName) { 2101 strncpy(g_appName, appName, (sizeof(g_appName) - 1)); 2102 } 2103 2104 if (appVersion) { 2105 strncpy(g_appVersion, appVersion, (sizeof(g_appVersion) - 1)); 2106 } 2107 2108 if (osName) { 2109 strncpy(g_osName, osName, (sizeof(g_osName) - 1)); 2110 } 2111 2112 LOGI("App Name: '%s', Version: '%s', OS: '%s'", g_appName, g_appVersion, g_osName); 2113 }