1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  * Copyright 2012 Joyent, Inc.  All rights reserved.
  25  */
  26 
  27 #include <regex.h>
  28 #include <devfsadm.h>
  29 #include <stdio.h>
  30 #include <strings.h>
  31 #include <stdlib.h>
  32 #include <limits.h>
  33 #include <ctype.h>
  34 
  35 /*
  36  * Note: separate from misc_link_i386.c because this will later
  37  * move to the gfx-drm gate.
  38  */
  39 
  40 static int agp_process(di_minor_t minor, di_node_t node);
  41 static int drm_node(di_minor_t minor, di_node_t node);
  42 
  43 static devfsadm_create_t drm_cbt[] = {
  44         { "drm", "ddi_display:drm", NULL,
  45             TYPE_EXACT, ILEVEL_0,       drm_node
  46         },
  47         { "agp", "ddi_agp:pseudo", NULL,
  48             TYPE_EXACT, ILEVEL_0, agp_process
  49         },
  50         { "agp", "ddi_agp:target", NULL,
  51             TYPE_EXACT, ILEVEL_0, agp_process
  52         },
  53         { "agp", "ddi_agp:cpugart", NULL,
  54             TYPE_EXACT, ILEVEL_0, agp_process
  55         },
  56         { "agp", "ddi_agp:master", NULL,
  57             TYPE_EXACT, ILEVEL_0, agp_process
  58         },
  59 };
  60 
  61 DEVFSADM_CREATE_INIT_V0(drm_cbt);
  62 
  63 /*
  64  * For debugging, run devfsadm like this:
  65  *  devfsadm -V drm_mid -V devfsadm:enum -c drm
  66  */
  67 static char *debug_mid = "drm_mid";
  68 
  69 typedef enum {
  70         DRIVER_AGPPSEUDO = 0,
  71         DRIVER_AGPTARGET,
  72         DRIVER_CPUGART,
  73         DRIVER_AGPMASTER_DRM_I915,
  74         DRIVER_AGPMASTER_DRM_RADEON,
  75         DRIVER_AGPMASTER_VGATEXT,
  76         DRIVER_UNKNOWN
  77 } driver_defs_t;
  78 
  79 typedef struct {
  80         char    *driver_name;
  81         int     index;
  82 } driver_name_table_entry_t;
  83 
  84 static driver_name_table_entry_t driver_name_table[] = {
  85         { "agpgart",            DRIVER_AGPPSEUDO },
  86         { "agptarget",          DRIVER_AGPTARGET },
  87         { "amd64_gart",         DRIVER_CPUGART },
  88         /* AGP master device managed by drm driver */
  89         { "i915",               DRIVER_AGPMASTER_DRM_I915 },
  90         { "radeon",             DRIVER_AGPMASTER_DRM_RADEON },
  91         { "vgatext",            DRIVER_AGPMASTER_VGATEXT },
  92         { NULL,                 DRIVER_UNKNOWN }
  93 };
  94 
  95 static devfsadm_enumerate_t agptarget_rules[1] =
  96         { "^agp$/^agptarget([0-9]+)$", 1, MATCH_ALL };
  97 static devfsadm_enumerate_t cpugart_rules[1] =
  98         { "^agp$/^cpugart([0-9]+)$", 1, MATCH_ALL };
  99 static devfsadm_enumerate_t agpmaster_rules[1] =
 100         { "^agp$/^agpmaster([0-9]+)$", 1, MATCH_ALL };
 101 static devfsadm_enumerate_t drm_rules[1] =
 102         { "^dri$/^card([0-9]+)$", 1, MATCH_ALL };
 103 
 104 
 105 /*
 106  * HOT auto cleanup of drm+agp links not desired.
 107  */
 108 static devfsadm_remove_t drm_remove_cbt[] = {
 109         { "agp", "^agpgart$", RM_POST,
 110                 ILEVEL_0, devfsadm_rm_all
 111         },
 112         { "agp", "^agp/agpmaster[0-9]+$", RM_POST,
 113                 ILEVEL_0, devfsadm_rm_all
 114         },
 115         { "agp", "^agp/agptarget[0-9]+$", RM_POST,
 116                 ILEVEL_0, devfsadm_rm_all
 117         },
 118         { "agp", "^agp/cpugart[0-9]+$", RM_POST,
 119                 ILEVEL_0, devfsadm_rm_all
 120         },
 121         { "drm", "^dri/card[0-9]+$", RM_POST,
 122                 ILEVEL_0, devfsadm_rm_all
 123         },
 124 };
 125 
 126 DEVFSADM_REMOVE_INIT_V0(drm_remove_cbt);
 127 
 128 static int
 129 agp_process(di_minor_t minor, di_node_t node)
 130 {
 131         char *minor_nm, *drv_nm;
 132         char *devfspath;
 133         char *I_path, *p_path, *buf;
 134         char *name = (char *)NULL;
 135         int i, index;
 136         devfsadm_enumerate_t rules[1];
 137 
 138         minor_nm = di_minor_name(minor);
 139         drv_nm = di_driver_name(node);
 140 
 141         if ((minor_nm == NULL) || (drv_nm == NULL)) {
 142                 return (DEVFSADM_CONTINUE);
 143         }
 144 
 145         devfsadm_print(debug_mid, "agp_process: minor=%s node=%s\n",
 146             minor_nm, di_node_name(node));
 147 
 148         devfspath = di_devfs_path(node);
 149         if (devfspath == NULL) {
 150                 devfsadm_print(debug_mid, "agp_process: devfspath is NULL\n");
 151                 return (DEVFSADM_CONTINUE);
 152         }
 153 
 154         I_path = (char *)malloc(PATH_MAX);
 155 
 156         if (I_path == NULL) {
 157                 di_devfs_path_free(devfspath);
 158                 devfsadm_print(debug_mid,  "agp_process: malloc failed\n");
 159                 return (DEVFSADM_CONTINUE);
 160         }
 161 
 162         p_path = (char *)malloc(PATH_MAX);
 163 
 164         if (p_path == NULL) {
 165                 devfsadm_print(debug_mid,  "agp_process: malloc failed\n");
 166                 di_devfs_path_free(devfspath);
 167                 free(I_path);
 168                 return (DEVFSADM_CONTINUE);
 169         }
 170 
 171         (void) strlcpy(p_path, devfspath, PATH_MAX);
 172         (void) strlcat(p_path, ":", PATH_MAX);
 173         (void) strlcat(p_path, minor_nm, PATH_MAX);
 174         di_devfs_path_free(devfspath);
 175 
 176         devfsadm_print(debug_mid, "agp_process: path %s\n", p_path);
 177 
 178         for (i = 0; ; i++) {
 179                 if ((driver_name_table[i].driver_name == NULL) ||
 180                     (strcmp(drv_nm, driver_name_table[i].driver_name) == 0)) {
 181                         index = driver_name_table[i].index;
 182                         break;
 183                 }
 184         }
 185         switch (index) {
 186         case DRIVER_AGPPSEUDO:
 187                 devfsadm_print(debug_mid,
 188                     "agp_process: psdeudo driver name\n");
 189                 name = "agpgart";
 190                 (void) snprintf(I_path, PATH_MAX, "%s", name);
 191                 devfsadm_print(debug_mid,
 192                     "mklink %s -> %s\n", I_path, p_path);
 193 
 194                 (void) devfsadm_mklink(I_path, node, minor, 0);
 195 
 196                 free(I_path);
 197                 free(p_path);
 198                 return (DEVFSADM_CONTINUE);
 199         case DRIVER_AGPTARGET:
 200                 devfsadm_print(debug_mid,
 201                     "agp_process: target driver name\n");
 202                 rules[0] = agptarget_rules[0];
 203                 name = "agptarget";
 204                 break;
 205         case DRIVER_CPUGART:
 206                 devfsadm_print(debug_mid,
 207                     "agp_process: cpugart driver name\n");
 208                 rules[0] = cpugart_rules[0];
 209                 name = "cpugart";
 210                 break;
 211         case DRIVER_AGPMASTER_DRM_I915:
 212         case DRIVER_AGPMASTER_DRM_RADEON:
 213         case DRIVER_AGPMASTER_VGATEXT:
 214                 devfsadm_print(debug_mid,
 215                     "agp_process: agpmaster driver name\n");
 216                 rules[0] = agpmaster_rules[0];
 217                 name = "agpmaster";
 218                 break;
 219         case DRIVER_UNKNOWN:
 220                 devfsadm_print(debug_mid,
 221                     "agp_process: unknown driver name=%s\n", drv_nm);
 222                 free(I_path);
 223                 free(p_path);
 224                 return (DEVFSADM_CONTINUE);
 225         }
 226 
 227         if (devfsadm_enumerate_int(p_path, 0, &buf, rules, 1)) {
 228                 devfsadm_print(debug_mid, "agp_process: exit/coninue\n");
 229                 free(I_path);
 230                 free(p_path);
 231                 return (DEVFSADM_CONTINUE);
 232         }
 233 
 234 
 235         (void) snprintf(I_path, PATH_MAX, "agp/%s%s", name, buf);
 236 
 237         devfsadm_print(debug_mid, "agp_process: p_path=%s buf=%s\n",
 238             p_path, buf);
 239 
 240         free(buf);
 241 
 242         devfsadm_print(debug_mid, "mklink %s -> %s\n", I_path, p_path);
 243 
 244         (void) devfsadm_mklink(I_path, node, minor, 0);
 245 
 246         free(p_path);
 247         free(I_path);
 248 
 249         return (DEVFSADM_CONTINUE);
 250 }
 251 
 252 static int
 253 drm_node(di_minor_t minor, di_node_t node)
 254 {
 255         char *minor_nm, *drv_nm;
 256         char *devfspath;
 257         char *I_path, *p_path, *buf;
 258         char *name = "card";
 259 
 260         minor_nm = di_minor_name(minor);
 261         drv_nm = di_driver_name(node);
 262         if ((minor_nm == NULL) || (drv_nm == NULL)) {
 263                 return (DEVFSADM_CONTINUE);
 264         }
 265 
 266         devfsadm_print(debug_mid, "drm_node: minor=%s node=%s type=%s\n",
 267             minor_nm, di_node_name(node), di_minor_nodetype(minor));
 268 
 269         devfspath = di_devfs_path(node);
 270         if (devfspath == NULL) {
 271                 devfsadm_print(debug_mid, "drm_node: devfspath is NULL\n");
 272                 return (DEVFSADM_CONTINUE);
 273         }
 274 
 275         I_path = (char *)malloc(PATH_MAX);
 276 
 277         if (I_path == NULL) {
 278                 di_devfs_path_free(devfspath);
 279                 devfsadm_print(debug_mid,  "drm_node: malloc failed\n");
 280                 return (DEVFSADM_CONTINUE);
 281         }
 282 
 283         p_path = (char *)malloc(PATH_MAX);
 284 
 285         if (p_path == NULL) {
 286                 devfsadm_print(debug_mid,  "drm_node: malloc failed\n");
 287                 di_devfs_path_free(devfspath);
 288                 free(I_path);
 289                 return (DEVFSADM_CONTINUE);
 290         }
 291 
 292         (void) strlcpy(p_path, devfspath, PATH_MAX);
 293         (void) strlcat(p_path, ":", PATH_MAX);
 294         (void) strlcat(p_path, minor_nm, PATH_MAX);
 295         di_devfs_path_free(devfspath);
 296 
 297         devfsadm_print(debug_mid, "drm_node: p_path %s\n", p_path);
 298 
 299         if (devfsadm_enumerate_int(p_path, 0, &buf, drm_rules, 1)) {
 300                 free(p_path);
 301                 devfsadm_print(debug_mid, "drm_node: exit/coninue\n");
 302                 return (DEVFSADM_CONTINUE);
 303         }
 304         (void) snprintf(I_path, PATH_MAX, "dri/%s%s", name, buf);
 305 
 306         devfsadm_print(debug_mid, "drm_node: p_path=%s buf=%s\n",
 307             p_path, buf);
 308 
 309         free(buf);
 310 
 311         devfsadm_print(debug_mid, "mklink %s -> %s\n", I_path, p_path);
 312         (void) devfsadm_mklink(I_path, node, minor, 0);
 313 
 314         free(p_path);
 315         free(I_path);
 316 
 317         return (0);
 318 }