diff options
author | Doug Ambrisko <ambrisko@FreeBSD.org> | 2006-05-05 16:10:45 +0000 |
---|---|---|
committer | Doug Ambrisko <ambrisko@FreeBSD.org> | 2006-05-05 16:10:45 +0000 |
commit | 060e48824720626ae71cbff2328ec91f4aab1839 (patch) | |
tree | c811b5cf32301e6302a0ae1c83535f7a7e3ba038 /sys/compat/linux/linux_util.c | |
parent | f28aa7244842452f52073676ff431b6b1e87b1b7 (diff) |
Enhance the Linux emulation layer to make MegaRAID SAS managements tool happy.
Add back in a scheme to emulate old type major/minor numbers via hooks into
stat, linprocfs to return major/minors that Linux app's expect. Currently
only /dev/null is always registered. Drivers can register via the Linux
type shim similar to the ioctl shim but by using
linux_device_register_handler/linux_device_unregister_handler functions.
The structure is:
struct linux_device_handler {
char *bsd_driver_name;
char *linux_driver_name;
char *bsd_device_name;
char *linux_device_name;
int linux_major;
int linux_minor;
int linux_char_device;
};
Linprocfs uses this to display the major number of the driver. The
soon to be available linsysfs will use it to fill in the driver name.
Linux_stat uses it to translate the major/minor into Linux type values.
Note major numbers are dynamically assigned via passing in a -1 for
the major number so we don't need to keep track of them.
This is somewhat needed due to us switching to our devfs. MegaCli
will not run until I add in the linsysfs and mfi Linux compat changes.
Sponsored by: IronPort Systems
Notes
Notes:
svn path=/head/; revision=158311
Diffstat (limited to 'sys/compat/linux/linux_util.c')
-rw-r--r-- | sys/compat/linux/linux_util.c | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/sys/compat/linux/linux_util.c b/sys/compat/linux/linux_util.c index b0a639167329..09c51311dc7c 100644 --- a/sys/compat/linux/linux_util.c +++ b/sys/compat/linux/linux_util.c @@ -33,8 +33,10 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> +#include <sys/bus.h> #include <sys/lock.h> #include <sys/malloc.h> +#include <sys/linker_set.h> #include <sys/mutex.h> #include <sys/namei.h> #include <sys/proc.h> @@ -82,3 +84,138 @@ linux_msg(const struct thread *td, const char *fmt, ...) va_end(ap); printf("\n"); } + +MALLOC_DECLARE(M_LINUX); + +struct device_element +{ + TAILQ_ENTRY(device_element) list; + struct linux_device_handler entry; +}; + +static TAILQ_HEAD(, device_element) devices = + TAILQ_HEAD_INITIALIZER(devices); + +static struct linux_device_handler null_handler = + { "mem", "mem", "null", "null", 1, 3, 1}; + +DATA_SET(linux_device_handler_set, null_handler); + +char * +linux_driver_get_name_dev(device_t dev) +{ + struct device_element *de; + const char *device_name = device_get_name(dev); + + if (device_name == NULL) + return NULL; + TAILQ_FOREACH(de, &devices, list) { + if (strcmp(device_name, de->entry.bsd_driver_name) == 0) + return (de->entry.linux_driver_name); + } + + return NULL; +} + +int +linux_driver_get_major_minor(char *node, int *major, int *minor) +{ + struct device_element *de; + + if (node == NULL || major == NULL || minor == NULL) + return 1; + TAILQ_FOREACH(de, &devices, list) { + if (strcmp(node, de->entry.bsd_device_name) == 0) { + *major = de->entry.linux_major; + *minor = de->entry.linux_minor; + return 0; + } + } + + return 1; +} + +char * +linux_get_char_devices() +{ + struct device_element *de; + char *temp, *string, *last; + char formated[256]; + int current_size = 0, string_size = 1024; + + MALLOC(string, char *, string_size, M_LINUX, M_WAITOK); + string[0] = '\000'; + last = ""; + TAILQ_FOREACH(de, &devices, list) { + if (!de->entry.linux_char_device) + continue; + temp = string; + if (strcmp(last, de->entry.bsd_driver_name) != 0) { + last = de->entry.bsd_driver_name; + + snprintf(formated, sizeof(formated), "%3d %s\n", + de->entry.linux_major, + de->entry.linux_device_name); + if (strlen(formated) + current_size + >= string_size) { + string_size *= 2; + MALLOC(string, char *, string_size, + M_LINUX, M_WAITOK); + bcopy(temp, string, current_size); + FREE(temp, M_LINUX); + } + strcat(string, formated); + current_size = strlen(string); + } + } + + return string; +} + +void +linux_free_get_char_devices(char *string) +{ + FREE(string, M_LINUX); +} + +static int linux_major_starting = 200; + +int +linux_device_register_handler(struct linux_device_handler *d) +{ + struct device_element *de; + + if (d == NULL) + return (EINVAL); + + MALLOC(de, struct device_element *, sizeof(*de), + M_LINUX, M_WAITOK); + if (d->linux_major < 0) { + d->linux_major = linux_major_starting++; + } + bcopy(d, &de->entry, sizeof(*d)); + + /* Add the element to the list, sorted on span. */ + TAILQ_INSERT_TAIL(&devices, de, list); + + return (0); +} + +int +linux_device_unregister_handler(struct linux_device_handler *d) +{ + struct device_element *de; + + if (d == NULL) + return (EINVAL); + + TAILQ_FOREACH(de, &devices, list) { + if (bcmp(d, &de->entry, sizeof(*d)) == 0) { + TAILQ_REMOVE(&devices, de, list); + FREE(de, M_LINUX); + return (0); + } + } + + return (EINVAL); +} |