4 * @brief Utils, used by Linux during driver installation/uninstallation.
6 * @author Copyright (C) 2007 - 2009 CERN. Yury GEORGIEVSKIY <ygeorgie@cern.ch>
10 * Helps to unify driver install procedure between two systems. All standart
11 * options are parsed here and group description is build-up.
12 * Module-specific options are parsed using defined module-specific vectors.
13 * Inspired by module-init-tools and rmmod, written by Rusty Russell. Thxs!
16 #include <general_both.h> /* for general definitions and macroses */
17 #include <general_ioctl.h> /* for _IO definitions */
20 /* sys_init_module system call */
21 extern long init_module(void *, unsigned long, const char *);
23 static void* grab_file(const char*, unsigned long*);
24 static const char* moderror(int);
25 static int find_device_nodes(int, char(*)[32], int);
26 static int disable_kernel_hotplug(char **);
27 static int set_kernel_hotplug(char *);
30 * @brief Disable udev/mdev
32 * @param ptr -- pointer address will hold disabled hotplug prog name
33 * (normally mdev/udev)
35 * If failed (i.e. returned -1) ptr is set to NULL
37 * @return 0 -- hotplug disabled
38 * @return -1 -- failed to disable hotplug
40 int disable_kernel_hotplug(char **ptr
)
43 int fd
= open("/proc/sys/kernel/hotplug", O_RDWR
);
48 perror("Can't open /proc/sys/kernel/hotplug");
52 if (read(fd
, buf
, sizeof(buf
)) == -1) {
54 perror("Can't read /proc/sys/kernel/hotplug");
58 asprintf(ptr
, "%s", buf
);
60 strcpy(buf
, ""); /* disable the usermode helper hotplug mechanism */
61 if (write(fd
, buf
, 2) == -1) {
64 perror("Can't write /proc/sys/kernel/hotplug");
74 * @brief Enable kernel hotplug helper
76 * @param hppath -- what to put as a hotplug helper for the kernel
77 * (mdev/udev normally)
82 * @return -1 -- FAILED
84 int set_kernel_hotplug(char *hppath
)
86 int fd
= open("/proc/sys/kernel/hotplug", O_WRONLY
);
89 perror("Can't open /proc/sys/kernel/hotplug");
93 if (write(fd
, hppath
, strlen(hppath
)) == -1) {
95 perror("Can't write /proc/sys/kernel/hotplug");
104 * @brief Extract Driver name from the .ko filename
106 * @param koptr -- .ko filename
108 * @b NOTE Returned pointer should be freed by the caller afterwards.
110 * @b Examples: /path/to/driver/tric.2.6.29.1-rt7.ko -> tric
111 * /path/to/driver/tric.ko -> tric
112 * ./tric.2.6.29.1-rt7.ko -> tric
113 * tric.2.6.29.1-rt7.ko -> tric
116 * @return driver name - if OK
117 * @return NULL - failed
119 static char* extract_driver_name(char *koptr
)
124 /* get rid of path if any */
125 ptr
= rindex(koptr
, '/');
129 /* should have suffix */
130 ptr
= strchr(koptr
, '.');
134 /* get rid of suffix and create driver name */
136 asprintf(&ptr
, "%.*s", am
, koptr
);
142 * @brief Lynx-like driver installation routine.
144 * @param drvr_fn -- driver path (.ko object)
145 * @param type -- @ref BLOCKDRIVER or @ref CHARDRIVER\n
146 * Special case is possible, where user can pass driver option
147 * address (casted to @e int) in a string representation.
148 * It will be passed to the init_module() system call as an
149 * option to the driver installation (3-rd parameter) instead
150 * of a standart one - which is a driver name.
152 * Will install the driver in the system. Supposed to be called @b ONLY once
153 * during installation procedure.
154 * @b NOTE If this routine will be called more then once during installation -
155 * then it will terminate installation programm
157 * @return driver ID (major number) - if successful
158 * @return -1 - if not
160 int dr_install(char *drvr_fn
, int type
)
162 char *drvr_nm
= NULL
; /* extracted driver name */
163 int rc
= 0; /* return code */
164 char *node_nm
= NULL
; /* driver node name */
166 void *drvrfile
= NULL
; /* our .ko */
169 char *options
= NULL
; /* driver option */
171 char buff
[32], *bufPtr
; /* for string parsing */
173 int devn
; /* major/minor device number */
174 char *hotplug
= NULL
;
176 drvr_nm
= extract_driver_name(drvr_fn
);
178 fprintf(stderr
, "Wrong [.ko] object file name\n");
182 /* put .ko in the local buffer */
183 if (!(drvrfile
= grab_file(drvr_fn
, &len
))) {
184 fprintf(stderr
, "Can't read '%s': %s\n",
185 drvr_fn
, strerror(errno
));
187 goto dr_install_exit
;
190 /* check for special case. Will overwrite standart one! */
191 if (!WITHIN_RANGE(CHARDRIVER
, type
, BLOCKDRIVER
))
192 /* this IS a special case. Setup user-defined
193 extra options along with driver name */
194 asprintf(&options
, "dname=%s %s", drvr_nm
, (char*)type
);
196 asprintf(&options
, "dname=%s", drvr_nm
);
199 disable_kernel_hotplug(&hotplug
); /* move mdev out-of-the-way */
201 /* insert module in the kernel */
202 if ( (ret
= init_module(drvrfile
, len
, options
)) != 0 ) {
203 fprintf(stderr
, "Error inserting '%s' module: %li %s\n",
204 drvr_fn
, ret
, moderror(errno
));
206 goto dr_install_exit
;
209 /* check if he made it */
210 if ( (procfd
= fopen("/proc/devices", "r")) == NULL
) {
211 mperr("Can't open /proc/devices");
213 goto dr_install_exit
;
216 /* search for the device driver entry */
218 while (fgets(buff
, sizeof(buff
), procfd
))
219 if (strstr(buff
, drvr_nm
)) { /* bingo! */
220 major_num
= atoi(strsep(&bufPtr
, " "));
224 /* check if we cool */
226 fprintf(stderr
, "'%s' device NOT found in procfs.\n", drvr_nm
);
228 goto dr_install_exit
;
230 printf("\n'%s' device driver installed. Major number %d.\n",
233 /* create service entry point */
234 asprintf(&node_nm
, "/dev/%s.0", drvr_nm
);
235 unlink(node_nm
); /* if already exist delete it */
236 devn
= makedev(major_num
, 0);
237 if (mknod(node_nm
, S_IFCHR
| 0666, devn
)) {
238 mperr("Can't create %s service node", node_nm
);
240 goto dr_install_exit
;
243 /* driverID (major num) will be used by the cdv_install() */
248 /* hotplug was disabled -- re-enable it back */
249 set_kernel_hotplug(hotplug
);
252 if (drvr_nm
) free(drvr_nm
);
253 if (options
) free(options
);
254 if (drvrfile
) free(drvrfile
);
255 if (node_nm
) free(node_nm
);
261 * @brief Lynx call wrapper. Will uninstall the driver in the system. !TODO!
263 * @param id - driver ID to uninstall
265 * Supposed to be called @b ONLY once during uninstallation.
269 int dr_uninstall(int id
)
276 * @brief Lynx call wrapper for character device installation.
278 * @param path -- info file
279 * @param driver_id -- driver ID (major number) returned by @e dr_install()
280 * @param extra -- extra parameter for the driver
282 * Normally this routine can be called several times from the driver
283 * installation procedure, namely as many times, as modules declared.
285 * In our case it can be called <B>only once</B> per driver (dr_install), so
286 * that only one statics table per driver is possible.
288 * In LynxOS, each time @e cdv_install() or @e bdv_install() are called by the
289 * user-space installation programm, driver installation vector from @b dldd
290 * structure is called. It returns statics table, that in turn is used in every
291 * entry point of @b dldd vector table.
293 * If @ref extra parameter is @b NOT zero - it will be used in
294 * @ref _GIOCTL_CDV_INSTALL ioctl call instead of @ref path parameter.
295 * I.e. they _CAN'T_ be used simultaneoulsy!
297 * @return major device ID - if successful.
298 * @return negative value - if not.
300 int cdv_install(char *path
, int driver_id
, int extra
)
302 static int hist
[32] = { 0 }; /* for whom i was already called */
303 int dfd
= -1; /* driver file descriptor */
306 char nn
[32]; /* device node name */
309 /* check, if called more then once for this driver */
311 if (hist
[cntr
] == driver_id
) {
312 fprintf(stderr
, "%s() is not allowed to be called more"
313 "than one time for the driver\n", __func__
);
319 hist
[cntr
] = driver_id
; /* save calling history */
321 if (!find_device_nodes(driver_id
, &nn
, 1)) {
322 fprintf(stderr
, "Can't find driver node (maj %d)\n",
327 if ((dfd
= open(nn
, O_RDWR
)) == -1) {
328 mperr("Can't open %s driver node", nn
);
332 disable_kernel_hotplug(&hp
); /* move mdev out-of-the-way */
334 if ( (maj_num
= ioctl(dfd
, _GIOCTL_CDV_INSTALL
,
335 (extra
)?(void*)extra
:path
)) < 0) {
336 mperr("_GIOCTL_CDV_INSTALL ioctl fails. Can't get major"
341 if (hp
) { set_kernel_hotplug(hp
); free(hp
); }
348 * @brief Driver uninstallation procedure. TODO.
350 * @param did - deviceID
354 int cdv_uninstall(int did
)
361 * @brief Read .ko and place it in the local buffer
363 * @param filename -- .ko file name
364 * @param size -- its size will be placed here
368 static void* grab_file(const char *filename
, unsigned long *size
)
370 unsigned int max
= 16384;
372 void *buffer
= malloc(max
);
376 if (streq(filename
, "-"))
377 fd
= dup(STDIN_FILENO
);
379 fd
= open(filename
, O_RDONLY
, 0);
385 while ((ret
= read(fd
, buffer
+ *size
, max
- *size
)) > 0) {
388 buffer
= realloc(buffer
, max
*= 2);
400 * @brief We use error numbers in a loose translation...
406 static const char* moderror(int err
)
410 return "Invalid module format";
412 return "Unknown symbol in module";
414 return "Module has wrong symbol version";
416 return "Invalid parameters";
418 return strerror(err
);
424 * @brief Search for device nodes by major in /dev directory.
426 * @param maj - major device ID to find nodes for
427 * @param nn - massive of the strings to store found device node names.
428 * Each string in this massive @b should be 32 bytes long.
429 * @param sz - massive size, i.e. how many strings of 32 bytes long are in
432 * @return Number of found device nodes.
434 static int find_device_nodes(int maj
, char (*nn
)[32], int sz
)
436 DIR *dir
= opendir("/dev");
437 struct direct
*direntry
= NULL
;
442 while ( (direntry
= readdir(dir
)) ) {
443 snprintf(fname
, sizeof(fname
), "/dev/%s", direntry
->d_name
);
445 /* st_rdev holds [Major | Minor] numbers */
446 if (major(fstat
.st_rdev
) == maj
) { /* bingo */
447 if (nn
&& (nodeCntr
< sz
))
448 strncpy(nn
[nodeCntr
], fname
,
449 sizeof(nn
[nodeCntr
]));