As with everything in the Linux world, hotplug support is moving very quickly. Hotplug is no longer a seperate package and it's functionality has been combined with udev, the dynamic device file daemon. In fact, everything I documented below is now obsolete on my systems except for /etc/fstab! I am now running Debian Sid (a.k.a. Unstable) on all my machines with kernel 2.6.14 or later, the latest udev, dbus, and hal packages. I am also running KDE 3.5 and by right-clicking the desktop, selecting configure, then Behavior from the next screen, then the device icons tab and finally checked "Show device icons". With this configuration my camera and USB stick just "pop up" and I can mount them. I do have /mnt/sda and /mnt/sdb enabled in my /etc/fstab. This works very well.
The trend in recent years with PC hardware, especially laptops, is toward hardware that isn't permanently wired or installed into the system. Several hardware standards have addressed this issue over the years with perhaps the most popular being PCMCIA (People Can't Remember Computer Industry Acronymns) in laptops. A few years back the PC industry came up with USB (Universal Serial Bus) and Apple computer came up with a similar standard called Firewire (IEEE 1394). Work has continued at the same time on support of these systems on Linux. The PCMCIA Card Services package has traditionally handled configuring 16 bit PCMCIA cards, but does not support the later 32 bit Cardbus (PCI) cards. This brings a new package called Hotplug to the rescue.
Hotplug is now the system responsible for receiving notification from the Linux kernel when a Cardbus card or USB device is inserted or removed. It does this through a traditional dæmon that receives the event notification and then calls a shell script based on the hardware class.
This page is not exhaustive on Hotplug and probably gets some things wrong, but It Works For Me(TM), so YMMV, etc. What I will describe are the scripts I created to mount my camera and memory stick at a consistent directory in my filesystem. At some point I will grapple with completing the Hotplug support for my wireless card and document it here.
In Debian the Hotplug package loads the kernel module(s) for the device on insertion. Unfortunately, that is as far as it goes, so a little scripting is involved. Fortunately, this isn't hard. Also, since I am running a 2.4 series kernel a bit of brute force is required. A smoother method may be available through the Usb-mount package (Debian Package). I have not tried Usb-mount prefering instead to learn a bit more about Hotplug itself. Let's get started.
I made the following changes to /etc/fstab:
none /proc/bus/usb usbfs defaults 0 0 /dev/sda1 /mnt/sda vfat user,noauto,gid=100,umask=000 0 0 /dev/sdb1 /mnt/sdb vfat user,noauto,gid=100,umask=000 0 0
The first line mounts a place for the USB device files (not to be confused with files in /dev) and the last two are the mount points that either my camera or memory stick will be mounted. The fourth field sets the mount options. In the case of /dev/sd[a|b]1 the user option allows a user to mount the partition (not really needed, but is nice for debugging). Since my normal login account is a member of the users group, the gid=100 option makes everything on the partition a part of the users group so I can read/write files on the partition. The umask=000 sets the permission bits of the vfat (MS-DOS formatted) partition. noauto prevents mounting of the partition at system startup. If execution of programs are desired on the partition, the exec must be specified.
The key to Hotplug being able configure USB devices beyond loading the kernel modules is the devices being listed in one of the map files. While there are several other files where the devices are listed, I will focus on writing a custom local.usermap file which will be read by Hotplug's /etc/hotplug/usb.agent script. The format of local.usermap is explained at WLUG-Wiki - Hot Plug notes page.
Here is my /etc/hotplug/usb/local.usermap
# usb module match_flags idVendor idProduct bcdDevice_lo bcdDevice_hi bDeviceClass bDeviceSubClass bDeviceProtocol bInterfaceClass bInterfaceSubClass bInterfaceProtocol driver_info # Lexar JumpDrive Sport lexar 0x0003 0x05dc 0xa400 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 # Fuji A330 digital camera fuji 0x0003 0x04cb 0x0148 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 # Lexar JumpDrive Pro lexar 0x0003 0x05dc 0xa410 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
The first column is the script that will be run on a match of the idVendor and idProduct colums. The match_flags column tells which of the columns to match. The remaining columns are set to 0x0 and are ignored.
Now, leaving the idProduct field at 0x0 would likely allow a generic loading of the proper driver for similar products. I guess it boils down to how fine of control you desire.
To learn the Vendor and product IDs, the lsusb utility from the usbutils package will help you discover these values quickly. Here is the output with my memory stick inserted:
$ lsusb Bus 001 Device 001: ID 0000:0000 Bus 001 Device 002: ID 05dc:a400 Lexar Media, Inc.
and with my camera inserted:
$ lsusb Bus 001 Device 001: ID 0000:0000 Bus 001 Device 003: ID 04cb:0148 Fuji Photo Film Co., Ltd
As you can see, the Vendor and Product IDs are readily visible.
To economize and to learn a bit more about shell scripting, I opted to use one script file for both of my devices with one script handling insertion and the other removal actions. I then used symbolic links with the names specified in the local.usermap pointing to the actual script. Here is the listing of my files:
$ ls -l /etc/hotplug/usb lrwxrwxrwx 1 root root 23 2004-11-22 14:47 fuji -> /etc/hotplug/usb/insert* lrwxrwxrwx 1 root root 23 2004-11-22 15:04 fuji.rmv -> /etc/hotplug/usb/remove* -rwxr-xr-x 1 root root 999 2004-11-23 07:33 insert* lrwxrwxrwx 1 root root 23 2004-11-22 14:47 lexar -> /etc/hotplug/usb/insert* lrwxrwxrwx 1 root root 23 2004-11-22 15:05 lexar.rmv -> /etc/hotplug/usb/remove* -rw-r--r-- 1 root root 356 2004-11-23 07:34 local.usermap -rwxr-xr-x 1 root root 402 2004-11-23 07:32 remove*
And now the insert script:
#!/bin/bash # This script is called by ../usb.agent which sets the environment # variables $ACTION, $DEVICE, and $REMOVER # Since I only have two storage devices, only /dev/sda or /dev/sdb will # be assigned. If more devices are possible just extend the script to # include them (sdc, sdd, etc) # A symlink is created using the $REMOVER variable that the ../usb.agent # script will execute on device removal. PATH=/sbin:/bin:/usr/sbin:/usr/bin BSNM=`basename $0` GROUP=users if [ "$ACTION" = "add" ] && [ -f "$DEVICE" ] then # check if $GROUP really exists if getent group $GROUP > /dev/null; then chmod 664 "$DEVICE" chown root.$GROUP "$DEVICE" fi fi if mount /mnt/sda > /dev/null 2>&1 then MOUNTDIR="sda" elif mount /mnt/sdb > /dev/null 2>&1 then MOUNTDIR="sdb" else exit 128 fi if [ "$BSNM" = "lexar" ] then MNT="stick" elif [ "$BSNM" = "fuji" ] then MNT="cam" else exit 127 fi mount --bind /mnt/$MOUNTDIR /mnt/$MNT ln -s /etc/hotplug/usb/$BSNM.rmv $REMOVER exit 0
For the script to work, the mount points (directories) of
/mnt/sda, /mnt/sdb, /mnt/cam, and
/mnt/stick will need to be created manually. The
$REMOVER
variable points to a file dynamically created in
/proc/bus/usb/001/ which corresponds the device number assigned
upon insertion. If /etc/hotplug/usb.agent finds
$REMOVER
, it calls that script upon device removal.
Here is /etc/hotplug/usb/remove:
#!/bin/sh # This file is linked to the $REMOVER variable set in ../usb.agent by # the ./insert script. # The mounts to be unmounted should match those in ./insert PATH=/sbin:/bin:/usr/sbin:/usr/bin LNK=`readlink $0` FLNM=`basename $LNK` if [ "$FLNM" = "lexar.rmv" ] then MNT="stick" elif [ "$FLNM" = "fuji.rmv" ] then MNT="cam" else exit 128 fi umount /mnt/$MNT umount /mnt/sda umount /mnt/sdb
The last three lines show that I can unmount one of the named directories by either variable substitution that is deduced from the name used to call the script (the readlink command is the key) or brute force. Since I've not learned how to find out what kernel device name is assigned upon insertion, I just force a mount/umount on either sda/sdb. When I learn how to do this I will make the scripts more elegant.
These scripts reliably mount my two devices at predictable mount points. Using the --bind option to mount(8) in the insert script allows having a consistent named mount point even though the device can actually be at either sda or sdb. If I were to run the 2.6 kernel series on my laptop, a Thinkpad 390E, with the udev package, much of this kludging probably wouldn't be necessary. However, I have great hardware support from the 2.4 series and these scripts work for me. If I were to have a third USB Storage device I would probably have to add sdc to my scripts.
Original content Copyright © 1997-2024 Nate Bargmann NØNB n0nb@n0nb.us
any other content copyright by respective author(s).
This page last modified January 20, 2006 |