[PATCH] Add subsystem support to mdev (firmware loading)

Erik Andersen andersen at codepoet.org
Thu Oct 19 13:11:26 PDT 2006


On Tue Oct 03, 2006 at 07:21:58PM +0200, Denis Vlasenko wrote:
> > I'm just going to reiterate how vital firmware loading is to a small  
> > but growing proportion of embedded Linux users, and hope that you see  
> > it as a valid addition to the codebase.
> > 
> > As well as the patch, a very simple script (also attached below)  
> > should be added to the examples directory to handle firmware loading.

I ended up needing to do some firmware loading myself, so I've
put together an alternative patch to handle firmware loading
within mdev.  My patch is a bit larger, but does not require any
external script (or even a shell), making it faster, better
suited for use in early userspace, and (most importantly) it does
what I happened to need yesterday.  I've tested it using a
usb speedtouch modem and an AIC-9405W SAS adapter and It Worked
For Me(tm).  So perhaps it might work for others as well...

On an unrelated note, somebody remind me why mdev lives under
util-linux as it has little (except portions of the end result)
in common with udev?  Any any reason not to have it install
itself as /sbin/hotplug or set /proc/sys/kernel/hotplug pointing
to /sbin/mdev when run with 'mdev -s'?

 -Erik

--
Erik B. Andersen             http://codepoet-consulting.com/
--This message was written using 73% post-consumer electrons--
-------------- next part --------------
Index: util-linux/mdev.c
===================================================================
--- util-linux/mdev.c	(revision 16402)
+++ util-linux/mdev.c	(working copy)
@@ -5,6 +5,7 @@
  *
  * Copyright 2005 Rob Landley <rob at landley.net>
  * Copyright 2005 Frank Sorenson <frank at tuxrocks.com>
+ * Copyright 2006 Erik Andersen <andersen at codepoet.org>
  *
  * Licensed under GPL version 2, see file LICENSE in this tarball for details.
  */
@@ -231,17 +232,16 @@
 
 int mdev_main(int argc, char *argv[])
 {
+	int status = 0;
 	char *action;
 	char *env_path;
 	RESERVE_CONFIG_BUFFER(temp,PATH_MAX);
 
 	xchdir(DEV_PATH);
 
-	/* Scan */
-
+	/* Scan for existing (aka coldplugged) devices */
 	if (argc == 2 && !strcmp(argv[1],"-s")) {
 		struct stat st;
-
 		xstat("/", &st);
 		bbg.root_major = major(st.st_dev);
 		bbg.root_minor = minor(st.st_dev);
@@ -249,20 +249,105 @@
 		find_dev(temp);
 		strcpy(temp,"/sys/class");
 		find_dev(temp);
-
-	/* Hotplug */
-
-	} else {
+	} else
+	{
+		/* Hotplug -- the kernel called us as /sbin/hotplug */
 		action = getenv("ACTION");
 		env_path = getenv("DEVPATH");
 		if (!action || !env_path)
 			bb_show_usage();
 
-		sprintf(temp, "/sys%s", env_path);
-		if (!strcmp(action, "add")) make_device(temp,0);
-		else if (!strcmp(action, "remove")) make_device(temp,1);
-	}
+		snprintf(temp, PATH_MAX, "/sys%s", env_path);
+		if (!strcmp(action, "remove")) make_device(temp,1);
+		else if (!strcmp(action, "add"))
+		{
+			char *firmware;
 
+			/* Create a device node to access the new device */
+			make_device(temp,0);
+
+			/* Check if we need to load some firmware in order to
+			 * make this particular device actually work */
+			firmware = getenv("FIRMWARE");
+			if (firmware)
+			{
+				int fd;
+				FILE *src, *dst;
+				struct stat st;
+				char echobuf[4];
+
+				/* First check if we even have the requested firmware
+				 * file waiting under /lib/firmware as there is not point
+				 * in doing anything further if the needed firmware is not
+				 * even present. */
+				snprintf(temp, PATH_MAX, "/lib/firmware/%s", firmware);
+				xstat(temp, &st);
+
+				/* sleep until the driver says it is ready for us to
+				 * upload the firmware.  Just as a safeguard, if the
+				 * driver takes longer than 30 seconds, give up... */
+				fd = 0;
+				snprintf(temp, PATH_MAX, "/sys%s/loading", env_path);
+				while(1) {
+					if (!stat(temp, &st) && S_ISREG(st.st_mode))
+						break;
+					sleep(1);
+					fd++;
+					if (fd >= 30) {
+						status = 1;
+						goto endgame;
+					}
+				}
+
+				/* We need to do an 'echo 1 > /sys/$DEVPATH/loading'
+				 * to tell the driver we are ready to upload the firmware */
+				fd = xopen(temp, O_WRONLY);
+				echobuf[0]='1'; echobuf[1]='\n';
+				if (write(fd, echobuf, 2) != 2) {
+					status = 1;
+					goto endgame;
+				}
+				close(fd);
+
+				/* Write the firmware to the driver's data file, i.e.
+				 * 'cat /lib/firmware/$FIRMWARE > /sys/$DEVPATH/data' */
+				status=-1;
+				snprintf(temp, PATH_MAX, "/lib/firmware/%s", firmware);
+				if ((src = bb_wfopen(temp, "r")) != NULL) {
+					snprintf(temp, PATH_MAX, "/sys%s/data", env_path);
+					if ((dst = bb_wfopen(temp, "w")) != NULL) {
+						int r = bb_copyfd_eof(fileno(src), fileno(dst));
+						fclose(src);
+						if (r > 0) {
+							status=0;
+						}
+					}
+				}
+
+				/* Tell the driver we are done messing with firmware */
+				snprintf(temp, PATH_MAX, "/sys%s/loading", env_path);
+				fd = xopen(temp, O_WRONLY);
+				if (status == 0) {
+					/* success -- do an 'echo 0 > /sys/$DEVPATH/loading' */
+					echobuf[0]='0'; echobuf[1]='\n';
+					if (write(fd, echobuf, 2) != 2) {
+						status = 1;
+						//goto endgame;
+					}
+				} else {
+					/* failure -- do an 'echo -1 > /sys/$DEVPATH/loading' */
+					echobuf[0]='-'; echobuf[1]='1'; echobuf[2]='\n';
+					if (write(fd, echobuf, 3) != 3) {
+						status = 1;
+						//goto endgame;
+					}
+				}
+				close(fd);
+			} // firmware
+		} // add action
+	} // hotplug
+
+endgame:
 	if (ENABLE_FEATURE_CLEAN_UP) RELEASE_CONFIG_BUFFER(temp);
-	return 0;
+	return status;
 }


More information about the busybox mailing list