/*  compat_name.c

    Compatibility name file for  devfsd  (build compatibility names).

    Copyright (C) 1998-2001  Richard Gooch

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    Richard Gooch may be reached by email at  rgooch@atnf.csiro.au
    The postal address is:
      Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
*/

/*
    This file contains functions to translate the kernel names in devfs to
    other names.


    Written by      Richard Gooch   30-APR-2000: Copied from devfsd.c

    Updated by      Richard Gooch   10-JUN-2000: Added compatibility entries
  for ISDN BRI driver. Thanks to Xuan Baldauf <xuan--lkml@baldauf.org>.

    Updated by      Chris Rankin    22-JAN-2001: Added entries for I2C devices.

    Updated by      Richard Gooch   30-JUL-2001: Added compatibility entries
  for Stallion serial driver. Updated compatibility support for joystick to
  take account of move from "joystick/%d" to "input/js%d".

    Last updated by Richard Gooch   7-AUG-2001: Created <write_old_sd_name> and
  supported SCSI discs 16 to 127.


*/
#include <linux/major.h>
#ifdef __KERNEL__
#  include <linux/string.h>
#  include <linux/kernel.h>
#  define atoi(ptr) simple_strtoul((ptr),NULL,10)
#else
#  include <stdio.h>
#  include <stdlib.h>
#  include <string.h>
#  include <ctype.h>
#endif

#ifndef IDE6_MAJOR        /*  In case we're building with an ancient kernel  */
#  define IDE6_MAJOR      88
#  define IDE7_MAJOR      89
#  define IDE8_MAJOR      90
#  define IDE9_MAJOR      91
#endif


static char get_old_ide_name (unsigned int major, unsigned int minor);
static char *write_old_sd_name (char *buffer,
				unsigned int major, unsigned int minor,
				char *part);


struct translate_struct
{
    char *match;    /*  The string to match to (up to length)                */
    char *format;   /*  Format of output, "%s" takes data past match string,
			NULL is effectively "%s" (just more efficient)       */
};

static struct translate_struct translate_table[] =
{
    {"sound/",     NULL},
    {"printers/",  "lp%s"},
    {"v4l/",       NULL},
    {"parports/",  "parport%s"},
    {"fb/",        "fb%s"},
    {"netlink/",   NULL},
    {"loop/",      "loop%s"},
    {"floppy/",    "fd%s"},
    {"rd/",        "ram%s"},
    {"md/",        "md%s"},
    {"cua/",       "cua%s"},
    {"tts/",       "ttyS%s"},
    {"ip2/",       "ip2%s"},
    {"ttf/",       "ttyF%s"},
    {"cuf/",       "cuf%s"},
    {"vc/",        "tty%s"},
    {"misc/",      NULL},
    {"isdn/",      NULL},
    {"i2c/",       "i2c-%s"},
    {"staliomem/", "staliomem%s"},  /*  Stallion serial driver               */
    {"ttyE/",      "ttyE%s"},       /*  Stallion serial driver               */
    {"cue/",       "cue%s"},        /*  Stallion serial driver               */
    {"input/js",   "js%s"},         /*  Joystick driver                      */
    {NULL,         NULL}
};

const char *get_old_name (const char *devname, unsigned int namelen,
			  char *buffer, unsigned int major, unsigned int minor)
/*  [SUMMARY] Translate a kernel-supplied name into an old name.
    <devname> The device name provided by the kernel.
    <namelen> The length of the name.
    <buffer> A buffer that may be used. This should be at least 128 bytes long.
    <major> The major number for the device.
    <minor> The minor number for the device.
    [RETURNS] A pointer to the old name if known, else NULL.
*/
{
    const char *compat_name = NULL;
    char *ptr;
    struct translate_struct *trans;

    for (trans = translate_table; trans->match != NULL; ++trans)
    {
	size_t len = strlen (trans->match);

	if (strncmp (devname, trans->match, len) == 0)
	{
	    if (trans->format == NULL) return (devname + len);
	    sprintf (buffer, trans->format, devname + len);
	    return (buffer);
	}
    }
    if (strncmp (devname, "sbp/", 4) == 0)
    {
	sprintf (buffer, "sbpcd%u", minor);
	compat_name = buffer;
    }
    else if ( (strncmp (devname, "scsi/", 5) == 0) &&
	      (strcmp (devname + namelen - 7, "generic") == 0) )
    {
	sprintf (buffer, "sg%u", minor);
	compat_name = buffer;
    }
    else if ( (strncmp (devname, "scsi/", 5) == 0) &&
	      (strncmp (ptr = (strrchr (devname, '/') + 1), "mt", 2)
	       == 0) )
    {
	char mode = ptr[2];

	if (mode == 'n') mode = '\0';
	sprintf (buffer, "nst%u%c", minor & 0x1f, mode);
	compat_name = buffer;
	if (devname[namelen - 1] != 'n') ++compat_name;
    }
    else if ( (strncmp (devname, "ide/", 4) == 0) &&
	      (strncmp (ptr = (strrchr (devname, '/') + 1), "mt", 2)
	       == 0) )
    {
	sprintf (buffer, "%sht%d", ptr + 2, minor & 0x7f);
	compat_name = buffer;
    }
    else if ( (strncmp (devname, "scsi/", 5) == 0) &&
	      (strcmp (devname + namelen - 2, "cd") == 0) )
    {
	sprintf (buffer, "sr%u", minor);
	compat_name = buffer;
    }
    else if ( (strncmp (devname, "scsi/", 5) == 0) &&
	      (strcmp (devname + namelen - 4, "disc") == 0) )
    {
	compat_name = write_old_sd_name (buffer, major, minor, "");
    }
    else if ( (strncmp (devname, "scsi/", 5) == 0) &&
	      (strncmp (ptr = (strrchr (devname, '/') +1), "part", 4)
	       == 0) )
    {
	compat_name = write_old_sd_name (buffer, major, minor, ptr + 4);
    }
    else if ( (strncmp (devname, "ide/", 4) == 0) &&
	      (strcmp (devname + namelen - 4, "disc") == 0) )
    {
	sprintf ( buffer, "hd%c",
		  get_old_ide_name (major, minor) );
	compat_name = buffer;
    }
    else if ( (strncmp (devname, "ide/", 4) == 0) &&
	      (strncmp (ptr = (strrchr (devname, '/') +1), "part", 4)
	       == 0) )
    {
	sprintf (buffer, "hd%c%s",
		 get_old_ide_name (major, minor), ptr + 4);
	compat_name = buffer;
    }
    else if ( (strncmp (devname, "ide/", 4) == 0) &&
	      (strcmp (devname + namelen - 2, "cd") == 0) )
    {
	sprintf ( buffer, "hd%c",
		  get_old_ide_name (major, minor) );
	compat_name = buffer;
    }
    else if (strncmp (devname, "vcc/", 4) == 0)
    {
	sprintf (buffer, "vcs%s", devname + 4);
	if (buffer[3] == '0') buffer[3] = '\0';
	compat_name = buffer;
    }
    else if (strncmp (devname, "pty/", 4) == 0)
    {
	int index = atoi (devname + 5);
	const char *pty1 = "pqrstuvwxyzabcde";
	const char *pty2 = "0123456789abcdef";

	sprintf (buffer, "%cty%c%c",
		 (devname[4] == 'm') ? 'p' : 't',
		 pty1[index >> 4], pty2[index & 0x0f]);
	compat_name = buffer;
    }
    return (compat_name);
}   /*  End Function get_old_name  */

static char get_old_ide_name (unsigned int major, unsigned int minor)
/*  [SUMMARY] Get the old IDE name for a device.
    <major> The major number for the device.
    <minor> The minor number for the device.
    [RETURNS] The drive letter.
*/
{
    char letter;

    switch (major)
    {
      case IDE0_MAJOR:
	letter = 'a';
	break;
      case IDE1_MAJOR:
	letter = 'c';
	break;
      case IDE2_MAJOR:
	letter = 'e';
	break;
      case IDE3_MAJOR:
	letter = 'g';
	break;
      case IDE4_MAJOR:
	letter = 'i';
	break;
      case IDE5_MAJOR:
	letter = 'k';
	break;
      case IDE6_MAJOR:
	letter = 'm';
	break;
      case IDE7_MAJOR:
	letter = 'o';
	break;
      case IDE8_MAJOR:
	letter = 'q';
	break;
      case IDE9_MAJOR:
	letter = 's';
	break;
      default:
	letter = 'y';
	break;
    }
    if (minor > 63) ++letter;
    return (letter);
}   /*  End Function get_old_ide_name  */

static char *write_old_sd_name (char *buffer,
				unsigned int major, unsigned int minor,
				char *part)
/*  [SUMMARY] Write the old SCSI disc name to a buffer.
    <buffer> The buffer to write to.
    <major> The major number for the device.
    <minor> The minor number for the device.
    <part> The partition string. Must be "" for a whole-disc entry.
    [RETURNS] A pointer to the buffer on success, else NULL.
*/
{
    unsigned int disc_index;

    if (major == 8)
    {
	sprintf (buffer, "sd%c%s", 'a' + (minor >> 4), part);
	return (buffer);
    }
    if ( (major > 64) && (major < 72) )
    {
	disc_index = ( (major - 64) << 4 ) + (minor >> 4);
	if (disc_index < 26)
	    sprintf (buffer, "sd%c%s", 'a' + disc_index, part);
	else sprintf (buffer, "sd%c%c%s",
		      'a' + (disc_index / 26) - 1, 'a' + disc_index, part);
	return (buffer);
    }
    return (NULL);
}   /*  End Function write_old_sd_name  */
