162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Parallel port device probing code 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: Carsten Gross, carsten@sol.wohnheim.uni-ulm.de 662306a36Sopenharmony_ci * Philip Blundell <philb@gnu.org> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/parport.h> 1162306a36Sopenharmony_ci#include <linux/string.h> 1262306a36Sopenharmony_ci#include <linux/string_helpers.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/uaccess.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic const struct { 1762306a36Sopenharmony_ci const char *token; 1862306a36Sopenharmony_ci const char *descr; 1962306a36Sopenharmony_ci} classes[] = { 2062306a36Sopenharmony_ci { "", "Legacy device" }, 2162306a36Sopenharmony_ci { "PRINTER", "Printer" }, 2262306a36Sopenharmony_ci { "MODEM", "Modem" }, 2362306a36Sopenharmony_ci { "NET", "Network device" }, 2462306a36Sopenharmony_ci { "HDC", "Hard disk" }, 2562306a36Sopenharmony_ci { "PCMCIA", "PCMCIA" }, 2662306a36Sopenharmony_ci { "MEDIA", "Multimedia device" }, 2762306a36Sopenharmony_ci { "FDC", "Floppy disk" }, 2862306a36Sopenharmony_ci { "PORTS", "Ports" }, 2962306a36Sopenharmony_ci { "SCANNER", "Scanner" }, 3062306a36Sopenharmony_ci { "DIGICAM", "Digital camera" }, 3162306a36Sopenharmony_ci { "", "Unknown device" }, 3262306a36Sopenharmony_ci { "", "Unspecified" }, 3362306a36Sopenharmony_ci { "SCSIADAPTER", "SCSI adapter" }, 3462306a36Sopenharmony_ci { NULL, NULL } 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic void pretty_print(struct parport *port, int device) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct parport_device_info *info = &port->probe_info[device + 1]; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci pr_info("%s", port->name); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (device >= 0) 4462306a36Sopenharmony_ci pr_cont(" (addr %d)", device); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci pr_cont(": %s", classes[info->class].descr); 4762306a36Sopenharmony_ci if (info->class) 4862306a36Sopenharmony_ci pr_cont(", %s %s", info->mfr, info->model); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci pr_cont("\n"); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void parse_data(struct parport *port, int device, char *str) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci char *txt = kmalloc(strlen(str)+1, GFP_KERNEL); 5662306a36Sopenharmony_ci char *p = txt, *q; 5762306a36Sopenharmony_ci int guessed_class = PARPORT_CLASS_UNSPEC; 5862306a36Sopenharmony_ci struct parport_device_info *info = &port->probe_info[device + 1]; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (!txt) { 6162306a36Sopenharmony_ci pr_warn("%s probe: memory squeeze\n", port->name); 6262306a36Sopenharmony_ci return; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci strcpy(txt, str); 6562306a36Sopenharmony_ci while (p) { 6662306a36Sopenharmony_ci char *sep; 6762306a36Sopenharmony_ci q = strchr(p, ';'); 6862306a36Sopenharmony_ci if (q) *q = 0; 6962306a36Sopenharmony_ci sep = strchr(p, ':'); 7062306a36Sopenharmony_ci if (sep) { 7162306a36Sopenharmony_ci char *u; 7262306a36Sopenharmony_ci *(sep++) = 0; 7362306a36Sopenharmony_ci /* Get rid of trailing blanks */ 7462306a36Sopenharmony_ci u = sep + strlen (sep) - 1; 7562306a36Sopenharmony_ci while (u >= p && *u == ' ') 7662306a36Sopenharmony_ci *u-- = '\0'; 7762306a36Sopenharmony_ci string_upper(p, p); 7862306a36Sopenharmony_ci if (!strcmp(p, "MFG") || !strcmp(p, "MANUFACTURER")) { 7962306a36Sopenharmony_ci kfree(info->mfr); 8062306a36Sopenharmony_ci info->mfr = kstrdup(sep, GFP_KERNEL); 8162306a36Sopenharmony_ci } else if (!strcmp(p, "MDL") || !strcmp(p, "MODEL")) { 8262306a36Sopenharmony_ci kfree(info->model); 8362306a36Sopenharmony_ci info->model = kstrdup(sep, GFP_KERNEL); 8462306a36Sopenharmony_ci } else if (!strcmp(p, "CLS") || !strcmp(p, "CLASS")) { 8562306a36Sopenharmony_ci int i; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci kfree(info->class_name); 8862306a36Sopenharmony_ci info->class_name = kstrdup(sep, GFP_KERNEL); 8962306a36Sopenharmony_ci string_upper(sep, sep); 9062306a36Sopenharmony_ci for (i = 0; classes[i].token; i++) { 9162306a36Sopenharmony_ci if (!strcmp(classes[i].token, sep)) { 9262306a36Sopenharmony_ci info->class = i; 9362306a36Sopenharmony_ci goto rock_on; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci pr_warn("%s probe: warning, class '%s' not understood\n", 9762306a36Sopenharmony_ci port->name, sep); 9862306a36Sopenharmony_ci info->class = PARPORT_CLASS_OTHER; 9962306a36Sopenharmony_ci } else if (!strcmp(p, "CMD") || 10062306a36Sopenharmony_ci !strcmp(p, "COMMAND SET")) { 10162306a36Sopenharmony_ci kfree(info->cmdset); 10262306a36Sopenharmony_ci info->cmdset = kstrdup(sep, GFP_KERNEL); 10362306a36Sopenharmony_ci /* if it speaks printer language, it's 10462306a36Sopenharmony_ci probably a printer */ 10562306a36Sopenharmony_ci if (strstr(sep, "PJL") || strstr(sep, "PCL")) 10662306a36Sopenharmony_ci guessed_class = PARPORT_CLASS_PRINTER; 10762306a36Sopenharmony_ci } else if (!strcmp(p, "DES") || !strcmp(p, "DESCRIPTION")) { 10862306a36Sopenharmony_ci kfree(info->description); 10962306a36Sopenharmony_ci info->description = kstrdup(sep, GFP_KERNEL); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci rock_on: 11362306a36Sopenharmony_ci if (q) 11462306a36Sopenharmony_ci p = q + 1; 11562306a36Sopenharmony_ci else 11662306a36Sopenharmony_ci p = NULL; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* If the device didn't tell us its class, maybe we have managed to 12062306a36Sopenharmony_ci guess one from the things it did say. */ 12162306a36Sopenharmony_ci if (info->class == PARPORT_CLASS_UNSPEC) 12262306a36Sopenharmony_ci info->class = guessed_class; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci pretty_print (port, device); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci kfree(txt); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* Read up to count-1 bytes of device id. Terminate buffer with 13062306a36Sopenharmony_ci * '\0'. Buffer begins with two Device ID length bytes as given by 13162306a36Sopenharmony_ci * device. */ 13262306a36Sopenharmony_cistatic ssize_t parport_read_device_id (struct parport *port, char *buffer, 13362306a36Sopenharmony_ci size_t count) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci unsigned char length[2]; 13662306a36Sopenharmony_ci unsigned lelen, belen; 13762306a36Sopenharmony_ci size_t idlens[4]; 13862306a36Sopenharmony_ci unsigned numidlens; 13962306a36Sopenharmony_ci unsigned current_idlen; 14062306a36Sopenharmony_ci ssize_t retval; 14162306a36Sopenharmony_ci size_t len; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* First two bytes are MSB,LSB of inclusive length. */ 14462306a36Sopenharmony_ci retval = parport_read (port, length, 2); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (retval < 0) 14762306a36Sopenharmony_ci return retval; 14862306a36Sopenharmony_ci if (retval != 2) 14962306a36Sopenharmony_ci return -EIO; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (count < 2) 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci memcpy(buffer, length, 2); 15462306a36Sopenharmony_ci len = 2; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Some devices wrongly send LE length, and some send it two 15762306a36Sopenharmony_ci * bytes short. Construct a sorted array of lengths to try. */ 15862306a36Sopenharmony_ci belen = (length[0] << 8) + length[1]; 15962306a36Sopenharmony_ci lelen = (length[1] << 8) + length[0]; 16062306a36Sopenharmony_ci idlens[0] = min(belen, lelen); 16162306a36Sopenharmony_ci idlens[1] = idlens[0]+2; 16262306a36Sopenharmony_ci if (belen != lelen) { 16362306a36Sopenharmony_ci int off = 2; 16462306a36Sopenharmony_ci /* Don't try lengths of 0x100 and 0x200 as 1 and 2 */ 16562306a36Sopenharmony_ci if (idlens[0] <= 2) 16662306a36Sopenharmony_ci off = 0; 16762306a36Sopenharmony_ci idlens[off] = max(belen, lelen); 16862306a36Sopenharmony_ci idlens[off+1] = idlens[off]+2; 16962306a36Sopenharmony_ci numidlens = off+2; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci else { 17262306a36Sopenharmony_ci /* Some devices don't truly implement Device ID, but 17362306a36Sopenharmony_ci * just return constant nibble forever. This catches 17462306a36Sopenharmony_ci * also those cases. */ 17562306a36Sopenharmony_ci if (idlens[0] == 0 || idlens[0] > 0xFFF) { 17662306a36Sopenharmony_ci printk(KERN_DEBUG "%s: reported broken Device ID length of %#zX bytes\n", 17762306a36Sopenharmony_ci port->name, idlens[0]); 17862306a36Sopenharmony_ci return -EIO; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci numidlens = 2; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Try to respect the given ID length despite all the bugs in 18462306a36Sopenharmony_ci * the ID length. Read according to shortest possible ID 18562306a36Sopenharmony_ci * first. */ 18662306a36Sopenharmony_ci for (current_idlen = 0; current_idlen < numidlens; ++current_idlen) { 18762306a36Sopenharmony_ci size_t idlen = idlens[current_idlen]; 18862306a36Sopenharmony_ci if (idlen+1 >= count) 18962306a36Sopenharmony_ci break; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci retval = parport_read (port, buffer+len, idlen-len); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (retval < 0) 19462306a36Sopenharmony_ci return retval; 19562306a36Sopenharmony_ci len += retval; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (port->physport->ieee1284.phase != IEEE1284_PH_HBUSY_DAVAIL) { 19862306a36Sopenharmony_ci if (belen != len) { 19962306a36Sopenharmony_ci printk(KERN_DEBUG "%s: Device ID was %zd bytes while device told it would be %d bytes\n", 20062306a36Sopenharmony_ci port->name, len, belen); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci goto done; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* This might end reading the Device ID too 20662306a36Sopenharmony_ci * soon. Hopefully the needed fields were already in 20762306a36Sopenharmony_ci * the first 256 bytes or so that we must have read so 20862306a36Sopenharmony_ci * far. */ 20962306a36Sopenharmony_ci if (buffer[len-1] == ';') { 21062306a36Sopenharmony_ci printk(KERN_DEBUG "%s: Device ID reading stopped before device told data not available. Current idlen %u of %u, len bytes %02X %02X\n", 21162306a36Sopenharmony_ci port->name, current_idlen, numidlens, 21262306a36Sopenharmony_ci length[0], length[1]); 21362306a36Sopenharmony_ci goto done; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci if (current_idlen < numidlens) { 21762306a36Sopenharmony_ci /* Buffer not large enough, read to end of buffer. */ 21862306a36Sopenharmony_ci size_t idlen, len2; 21962306a36Sopenharmony_ci if (len+1 < count) { 22062306a36Sopenharmony_ci retval = parport_read (port, buffer+len, count-len-1); 22162306a36Sopenharmony_ci if (retval < 0) 22262306a36Sopenharmony_ci return retval; 22362306a36Sopenharmony_ci len += retval; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci /* Read the whole ID since some devices would not 22662306a36Sopenharmony_ci * otherwise give back the Device ID from beginning 22762306a36Sopenharmony_ci * next time when asked. */ 22862306a36Sopenharmony_ci idlen = idlens[current_idlen]; 22962306a36Sopenharmony_ci len2 = len; 23062306a36Sopenharmony_ci while(len2 < idlen && retval > 0) { 23162306a36Sopenharmony_ci char tmp[4]; 23262306a36Sopenharmony_ci retval = parport_read (port, tmp, 23362306a36Sopenharmony_ci min(sizeof tmp, idlen-len2)); 23462306a36Sopenharmony_ci if (retval < 0) 23562306a36Sopenharmony_ci return retval; 23662306a36Sopenharmony_ci len2 += retval; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci /* In addition, there are broken devices out there that don't 24062306a36Sopenharmony_ci even finish off with a semi-colon. We do not need to care 24162306a36Sopenharmony_ci about those at this time. */ 24262306a36Sopenharmony_ci done: 24362306a36Sopenharmony_ci buffer[len] = '\0'; 24462306a36Sopenharmony_ci return len; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* Get Std 1284 Device ID. */ 24862306a36Sopenharmony_cissize_t parport_device_id (int devnum, char *buffer, size_t count) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci ssize_t retval = -ENXIO; 25162306a36Sopenharmony_ci struct pardevice *dev = parport_open(devnum, daisy_dev_name); 25262306a36Sopenharmony_ci if (!dev) 25362306a36Sopenharmony_ci return -ENXIO; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci parport_claim_or_block (dev); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* Negotiate to compatibility mode, and then to device ID 25862306a36Sopenharmony_ci * mode. (This so that we start form beginning of device ID if 25962306a36Sopenharmony_ci * already in device ID mode.) */ 26062306a36Sopenharmony_ci parport_negotiate (dev->port, IEEE1284_MODE_COMPAT); 26162306a36Sopenharmony_ci retval = parport_negotiate (dev->port, 26262306a36Sopenharmony_ci IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (!retval) { 26562306a36Sopenharmony_ci retval = parport_read_device_id (dev->port, buffer, count); 26662306a36Sopenharmony_ci parport_negotiate (dev->port, IEEE1284_MODE_COMPAT); 26762306a36Sopenharmony_ci if (retval > 2) 26862306a36Sopenharmony_ci parse_data (dev->port, dev->daisy, buffer+2); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci parport_release (dev); 27262306a36Sopenharmony_ci parport_close (dev); 27362306a36Sopenharmony_ci return retval; 27462306a36Sopenharmony_ci} 275