Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Parallel port device probing code
0004  *
0005  * Authors:    Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
0006  *             Philip Blundell <philb@gnu.org>
0007  */
0008 
0009 #include <linux/module.h>
0010 #include <linux/parport.h>
0011 #include <linux/string.h>
0012 #include <linux/string_helpers.h>
0013 #include <linux/slab.h>
0014 #include <linux/uaccess.h>
0015 
0016 static const struct {
0017     const char *token;
0018     const char *descr;
0019 } classes[] = {
0020     { "",            "Legacy device" },
0021     { "PRINTER",     "Printer" },
0022     { "MODEM",       "Modem" },
0023     { "NET",         "Network device" },
0024     { "HDC",         "Hard disk" },
0025     { "PCMCIA",      "PCMCIA" },
0026     { "MEDIA",       "Multimedia device" },
0027     { "FDC",         "Floppy disk" },
0028     { "PORTS",       "Ports" },
0029     { "SCANNER",     "Scanner" },
0030     { "DIGICAM",     "Digital camera" },
0031     { "",            "Unknown device" },
0032     { "",            "Unspecified" },
0033     { "SCSIADAPTER", "SCSI adapter" },
0034     { NULL,          NULL }
0035 };
0036 
0037 static void pretty_print(struct parport *port, int device)
0038 {
0039     struct parport_device_info *info = &port->probe_info[device + 1];
0040 
0041     pr_info("%s", port->name);
0042 
0043     if (device >= 0)
0044         pr_cont(" (addr %d)", device);
0045 
0046     pr_cont(": %s", classes[info->class].descr);
0047     if (info->class)
0048         pr_cont(", %s %s", info->mfr, info->model);
0049 
0050     pr_cont("\n");
0051 }
0052 
0053 static void parse_data(struct parport *port, int device, char *str)
0054 {
0055     char *txt = kmalloc(strlen(str)+1, GFP_KERNEL);
0056     char *p = txt, *q;
0057     int guessed_class = PARPORT_CLASS_UNSPEC;
0058     struct parport_device_info *info = &port->probe_info[device + 1];
0059 
0060     if (!txt) {
0061         pr_warn("%s probe: memory squeeze\n", port->name);
0062         return;
0063     }
0064     strcpy(txt, str);
0065     while (p) {
0066         char *sep;
0067         q = strchr(p, ';');
0068         if (q) *q = 0;
0069         sep = strchr(p, ':');
0070         if (sep) {
0071             char *u;
0072             *(sep++) = 0;
0073             /* Get rid of trailing blanks */
0074             u = sep + strlen (sep) - 1;
0075             while (u >= p && *u == ' ')
0076                 *u-- = '\0';
0077             string_upper(p, p);
0078             if (!strcmp(p, "MFG") || !strcmp(p, "MANUFACTURER")) {
0079                 kfree(info->mfr);
0080                 info->mfr = kstrdup(sep, GFP_KERNEL);
0081             } else if (!strcmp(p, "MDL") || !strcmp(p, "MODEL")) {
0082                 kfree(info->model);
0083                 info->model = kstrdup(sep, GFP_KERNEL);
0084             } else if (!strcmp(p, "CLS") || !strcmp(p, "CLASS")) {
0085                 int i;
0086 
0087                 kfree(info->class_name);
0088                 info->class_name = kstrdup(sep, GFP_KERNEL);
0089                 string_upper(sep, sep);
0090                 for (i = 0; classes[i].token; i++) {
0091                     if (!strcmp(classes[i].token, sep)) {
0092                         info->class = i;
0093                         goto rock_on;
0094                     }
0095                 }
0096                 pr_warn("%s probe: warning, class '%s' not understood\n",
0097                     port->name, sep);
0098                 info->class = PARPORT_CLASS_OTHER;
0099             } else if (!strcmp(p, "CMD") ||
0100                    !strcmp(p, "COMMAND SET")) {
0101                 kfree(info->cmdset);
0102                 info->cmdset = kstrdup(sep, GFP_KERNEL);
0103                 /* if it speaks printer language, it's
0104                    probably a printer */
0105                 if (strstr(sep, "PJL") || strstr(sep, "PCL"))
0106                     guessed_class = PARPORT_CLASS_PRINTER;
0107             } else if (!strcmp(p, "DES") || !strcmp(p, "DESCRIPTION")) {
0108                 kfree(info->description);
0109                 info->description = kstrdup(sep, GFP_KERNEL);
0110             }
0111         }
0112     rock_on:
0113         if (q)
0114             p = q + 1;
0115         else
0116             p = NULL;
0117     }
0118 
0119     /* If the device didn't tell us its class, maybe we have managed to
0120        guess one from the things it did say. */
0121     if (info->class == PARPORT_CLASS_UNSPEC)
0122         info->class = guessed_class;
0123 
0124     pretty_print (port, device);
0125 
0126     kfree(txt);
0127 }
0128 
0129 /* Read up to count-1 bytes of device id. Terminate buffer with
0130  * '\0'. Buffer begins with two Device ID length bytes as given by
0131  * device. */
0132 static ssize_t parport_read_device_id (struct parport *port, char *buffer,
0133                        size_t count)
0134 {
0135     unsigned char length[2];
0136     unsigned lelen, belen;
0137     size_t idlens[4];
0138     unsigned numidlens;
0139     unsigned current_idlen;
0140     ssize_t retval;
0141     size_t len;
0142 
0143     /* First two bytes are MSB,LSB of inclusive length. */
0144     retval = parport_read (port, length, 2);
0145 
0146     if (retval < 0)
0147         return retval;
0148     if (retval != 2)
0149         return -EIO;
0150 
0151     if (count < 2)
0152         return 0;
0153     memcpy(buffer, length, 2);
0154     len = 2;
0155 
0156     /* Some devices wrongly send LE length, and some send it two
0157      * bytes short. Construct a sorted array of lengths to try. */
0158     belen = (length[0] << 8) + length[1];
0159     lelen = (length[1] << 8) + length[0];
0160     idlens[0] = min(belen, lelen);
0161     idlens[1] = idlens[0]+2;
0162     if (belen != lelen) {
0163         int off = 2;
0164         /* Don't try lengths of 0x100 and 0x200 as 1 and 2 */
0165         if (idlens[0] <= 2)
0166             off = 0;
0167         idlens[off] = max(belen, lelen);
0168         idlens[off+1] = idlens[off]+2;
0169         numidlens = off+2;
0170     }
0171     else {
0172         /* Some devices don't truly implement Device ID, but
0173          * just return constant nibble forever. This catches
0174          * also those cases. */
0175         if (idlens[0] == 0 || idlens[0] > 0xFFF) {
0176             printk(KERN_DEBUG "%s: reported broken Device ID length of %#zX bytes\n",
0177                    port->name, idlens[0]);
0178             return -EIO;
0179         }
0180         numidlens = 2;
0181     }
0182 
0183     /* Try to respect the given ID length despite all the bugs in
0184      * the ID length. Read according to shortest possible ID
0185      * first. */
0186     for (current_idlen = 0; current_idlen < numidlens; ++current_idlen) {
0187         size_t idlen = idlens[current_idlen];
0188         if (idlen+1 >= count)
0189             break;
0190 
0191         retval = parport_read (port, buffer+len, idlen-len);
0192 
0193         if (retval < 0)
0194             return retval;
0195         len += retval;
0196 
0197         if (port->physport->ieee1284.phase != IEEE1284_PH_HBUSY_DAVAIL) {
0198             if (belen != len) {
0199                 printk(KERN_DEBUG "%s: Device ID was %zd bytes while device told it would be %d bytes\n",
0200                        port->name, len, belen);
0201             }
0202             goto done;
0203         }
0204 
0205         /* This might end reading the Device ID too
0206          * soon. Hopefully the needed fields were already in
0207          * the first 256 bytes or so that we must have read so
0208          * far. */
0209         if (buffer[len-1] == ';') {
0210             printk(KERN_DEBUG "%s: Device ID reading stopped before device told data not available. Current idlen %u of %u, len bytes %02X %02X\n",
0211                    port->name, current_idlen, numidlens,
0212                    length[0], length[1]);
0213             goto done;
0214         }
0215     }
0216     if (current_idlen < numidlens) {
0217         /* Buffer not large enough, read to end of buffer. */
0218         size_t idlen, len2;
0219         if (len+1 < count) {
0220             retval = parport_read (port, buffer+len, count-len-1);
0221             if (retval < 0)
0222                 return retval;
0223             len += retval;
0224         }
0225         /* Read the whole ID since some devices would not
0226          * otherwise give back the Device ID from beginning
0227          * next time when asked. */
0228         idlen = idlens[current_idlen];
0229         len2 = len;
0230         while(len2 < idlen && retval > 0) {
0231             char tmp[4];
0232             retval = parport_read (port, tmp,
0233                            min(sizeof tmp, idlen-len2));
0234             if (retval < 0)
0235                 return retval;
0236             len2 += retval;
0237         }
0238     }
0239     /* In addition, there are broken devices out there that don't
0240        even finish off with a semi-colon. We do not need to care
0241        about those at this time. */
0242  done:
0243     buffer[len] = '\0';
0244     return len;
0245 }
0246 
0247 /* Get Std 1284 Device ID. */
0248 ssize_t parport_device_id (int devnum, char *buffer, size_t count)
0249 {
0250     ssize_t retval = -ENXIO;
0251     struct pardevice *dev = parport_open(devnum, daisy_dev_name);
0252     if (!dev)
0253         return -ENXIO;
0254 
0255     parport_claim_or_block (dev);
0256 
0257     /* Negotiate to compatibility mode, and then to device ID
0258      * mode. (This so that we start form beginning of device ID if
0259      * already in device ID mode.) */
0260     parport_negotiate (dev->port, IEEE1284_MODE_COMPAT);
0261     retval = parport_negotiate (dev->port,
0262                     IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID);
0263 
0264     if (!retval) {
0265         retval = parport_read_device_id (dev->port, buffer, count);
0266         parport_negotiate (dev->port, IEEE1284_MODE_COMPAT);
0267         if (retval > 2)
0268             parse_data (dev->port, dev->daisy, buffer+2);
0269     }
0270 
0271     parport_release (dev);
0272     parport_close (dev);
0273     return retval;
0274 }