/*1* IEEE-1284 support functions for CUPS.2*3* Copyright © 2007-2015 by Apple Inc.4* Copyright © 1997-2007 by Easy Software Products, all rights reserved.5*6* Licensed under Apache License v2.0. See the file "LICENSE" for more7* information.8*/910/*11* Include necessary headers.12*/1314#include "backend-private.h"15#include <cups/ppd-private.h>161718/*19* 'backendGetDeviceID()' - Get the IEEE-1284 device ID string and20* corresponding URI.21*/2223int /* O - 0 on success, -1 on failure */24backendGetDeviceID(25int fd, /* I - File descriptor */26char *device_id, /* O - 1284 device ID */27int device_id_size, /* I - Size of buffer */28char *make_model, /* O - Make/model */29int make_model_size, /* I - Size of buffer */30const char *scheme, /* I - URI scheme */31char *uri, /* O - Device URI */32int uri_size) /* I - Size of buffer */33{34#ifdef __APPLE__ /* This function is a no-op */35(void)fd;36(void)device_id;37(void)device_id_size;38(void)make_model;39(void)make_model_size;40(void)scheme;41(void)uri;42(void)uri_size;4344return (-1);4546#else /* Get the device ID from the specified file descriptor... */47# ifdef __linux48int length; /* Length of device ID info */49int got_id = 0;50# endif /* __linux */51# if defined(__sun) && defined(ECPPIOC_GETDEVID)52struct ecpp_device_id did; /* Device ID buffer */53# endif /* __sun && ECPPIOC_GETDEVID */54char *ptr; /* Pointer into device ID */555657/*58* Range check input...59*/6061if (!device_id || device_id_size < 32)62{63return (-1);64}6566if (make_model)67*make_model = '\0';6869if (fd >= 0)70{71/*72* Get the device ID string...73*/7475*device_id = '\0';7677# ifdef __linux78if (ioctl(fd, LPIOC_GET_DEVICE_ID((unsigned)device_id_size), device_id))79{80/*81* Linux has to implement things differently for every device it seems.82* Since the standard parallel port driver does not provide a simple83* ioctl() to get the 1284 device ID, we have to open the "raw" parallel84* device corresponding to this port and do some negotiation trickery85* to get the current device ID.86*/8788if (uri && !strncmp(uri, "parallel:/dev/", 14))89{90char devparport[16]; /* /dev/parportN */91int devparportfd, /* File descriptor for raw device */92mode; /* Port mode */939495/*96* Since the Linux parallel backend only supports 4 parallel port97* devices, just grab the trailing digit and use it to construct a98* /dev/parportN filename...99*/100101snprintf(devparport, sizeof(devparport), "/dev/parport%s",102uri + strlen(uri) - 1);103104if ((devparportfd = open(devparport, O_RDWR | O_NOCTTY)) != -1)105{106/*107* Claim the device...108*/109110if (!ioctl(devparportfd, PPCLAIM))111{112fcntl(devparportfd, F_SETFL, fcntl(devparportfd, F_GETFL) | O_NONBLOCK);113114mode = IEEE1284_MODE_COMPAT;115116if (!ioctl(devparportfd, PPNEGOT, &mode))117{118/*119* Put the device into Device ID mode...120*/121122mode = IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID;123124if (!ioctl(devparportfd, PPNEGOT, &mode))125{126/*127* Read the 1284 device ID...128*/129130if ((length = read(devparportfd, device_id, (size_t)device_id_size - 1)) >= 2)131{132device_id[length] = '\0';133got_id = 1;134}135}136}137138/*139* Release the device...140*/141142ioctl(devparportfd, PPRELEASE);143}144145close(devparportfd);146}147}148}149else150got_id = 1;151152if (got_id)153{154/*155* Extract the length of the device ID string from the first two156* bytes. The 1284 spec says the length is stored MSB first...157*/158159length = (int)((((unsigned)device_id[0] & 255) << 8) + ((unsigned)device_id[1] & 255));160161/*162* Check to see if the length is larger than our buffer; first163* assume that the vendor incorrectly implemented the 1284 spec,164* and then limit the length to the size of our buffer...165*/166167if (length > device_id_size || length < 14)168length = (int)((((unsigned)device_id[1] & 255) << 8) + ((unsigned)device_id[0] & 255));169170if (length > device_id_size)171length = device_id_size;172173/*174* The length field counts the number of bytes in the string175* including the length field itself (2 bytes). The minimum176* length for a valid/usable device ID is 14 bytes:177*178* <LENGTH> MFG: <MFG> ;MDL: <MDL> ;179* 2 + 4 + 1 + 5 + 1 + 1180*/181182if (length < 14)183{184/*185* Can't use this device ID, so don't try to copy it...186*/187188device_id[0] = '\0';189got_id = 0;190}191else192{193/*194* Copy the device ID text to the beginning of the buffer and195* nul-terminate.196*/197198length -= 2;199200memmove(device_id, device_id + 2, (size_t)length);201device_id[length] = '\0';202}203}204else205{206*device_id = '\0';207}208# endif /* __linux */209210# if defined(__sun) && defined(ECPPIOC_GETDEVID)211did.mode = ECPP_CENTRONICS;212did.len = device_id_size - 1;213did.rlen = 0;214did.addr = device_id;215216if (!ioctl(fd, ECPPIOC_GETDEVID, &did))217{218/*219* Nul-terminate the device ID text.220*/221222if (did.rlen < (device_id_size - 1))223device_id[did.rlen] = '\0';224else225device_id[device_id_size - 1] = '\0';226}227# endif /* __sun && ECPPIOC_GETDEVID */228}229230/*231* Check whether device ID is valid. Turn line breaks and tabs to spaces and232* reject device IDs with non-printable characters.233*/234235for (ptr = device_id; *ptr; ptr ++)236if (_cups_isspace(*ptr))237*ptr = ' ';238else if ((*ptr & 255) < ' ' || *ptr == 127)239{240*device_id = '\0';241break;242}243244if (scheme && uri)245*uri = '\0';246247if (!*device_id)248return (-1);249250/*251* Get the make and model...252*/253254if (make_model)255backendGetMakeModel(device_id, make_model, (size_t)make_model_size);256257/*258* Then generate a device URI...259*/260261if (scheme && uri && uri_size > 32)262{263int num_values; /* Number of keys and values */264cups_option_t *values; /* Keys and values in device ID */265const char *mfg, /* Manufacturer */266*mdl, /* Model */267*sern; /* Serial number */268char temp[256], /* Temporary manufacturer string */269*tempptr; /* Pointer into temp string */270271272/*273* Get the make, model, and serial numbers...274*/275276num_values = _cupsGet1284Values(device_id, &values);277278if ((sern = cupsGetOption("SERIALNUMBER", num_values, values)) == NULL)279if ((sern = cupsGetOption("SERN", num_values, values)) == NULL)280sern = cupsGetOption("SN", num_values, values);281282if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL)283mfg = cupsGetOption("MFG", num_values, values);284285if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL)286mdl = cupsGetOption("MDL", num_values, values);287288if (mfg)289{290if (!_cups_strcasecmp(mfg, "Hewlett-Packard"))291mfg = "HP";292else if (!_cups_strcasecmp(mfg, "Lexmark International"))293mfg = "Lexmark";294}295else296{297strlcpy(temp, make_model, sizeof(temp));298299if ((tempptr = strchr(temp, ' ')) != NULL)300*tempptr = '\0';301302mfg = temp;303}304305if (!mdl)306mdl = "";307308if (!_cups_strncasecmp(mdl, mfg, strlen(mfg)))309{310mdl += strlen(mfg);311312while (isspace(*mdl & 255))313mdl ++;314}315316/*317* Generate the device URI from the manufacturer, make_model, and318* serial number strings.319*/320321httpAssembleURIf(HTTP_URI_CODING_ALL, uri, uri_size, scheme, NULL, mfg, 0,322"/%s%s%s", mdl, sern ? "?serial=" : "", sern ? sern : "");323324cupsFreeOptions(num_values, values);325}326327return (0);328#endif /* __APPLE__ */329}330331332/*333* 'backendGetMakeModel()' - Get the make and model string from the device ID.334*/335336int /* O - 0 on success, -1 on failure */337backendGetMakeModel(338const char *device_id, /* O - 1284 device ID */339char *make_model, /* O - Make/model */340size_t make_model_size) /* I - Size of buffer */341{342int num_values; /* Number of keys and values */343cups_option_t *values; /* Keys and values */344const char *mfg, /* Manufacturer string */345*mdl, /* Model string */346*des; /* Description string */347348349/*350* Range check input...351*/352353if (!device_id || !*device_id || !make_model || make_model_size < 32)354return (-1);355356*make_model = '\0';357358/*359* Look for the description field...360*/361362num_values = _cupsGet1284Values(device_id, &values);363364if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL)365mdl = cupsGetOption("MDL", num_values, values);366367if (mdl)368{369/*370* Build a make-model string from the manufacturer and model attributes...371*/372373if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL)374mfg = cupsGetOption("MFG", num_values, values);375376if (!mfg || !_cups_strncasecmp(mdl, mfg, strlen(mfg)))377{378/*379* Just copy the model string, since it has the manufacturer...380*/381382_ppdNormalizeMakeAndModel(mdl, make_model, make_model_size);383}384else385{386/*387* Concatenate the make and model...388*/389390char temp[1024]; /* Temporary make and model */391392snprintf(temp, sizeof(temp), "%s %s", mfg, mdl);393394_ppdNormalizeMakeAndModel(temp, make_model, make_model_size);395}396}397else if ((des = cupsGetOption("DESCRIPTION", num_values, values)) != NULL ||398(des = cupsGetOption("DES", num_values, values)) != NULL)399{400/*401* Make sure the description contains something useful, since some402* printer manufacturers (HP) apparently don't follow the standards403* they helped to define...404*405* Here we require the description to be 8 or more characters in length,406* containing at least one space and one letter.407*/408409if (strlen(des) >= 8)410{411const char *ptr; /* Pointer into description */412int letters, /* Number of letters seen */413spaces; /* Number of spaces seen */414415416for (ptr = des, letters = 0, spaces = 0; *ptr; ptr ++)417{418if (isspace(*ptr & 255))419spaces ++;420else if (isalpha(*ptr & 255))421letters ++;422423if (spaces && letters)424break;425}426427if (spaces && letters)428_ppdNormalizeMakeAndModel(des, make_model, make_model_size);429}430}431432if (!make_model[0])433{434/*435* Use "Unknown" as the printer make and model...436*/437438strlcpy(make_model, "Unknown", make_model_size);439}440441cupsFreeOptions(num_values, values);442443return (0);444}445446447