FreeBSD and USB Thumbdrives

USB thumbdrives are a convenient way of moving relatively small volumes of data around machines. FreeBSD fully supports such devices. Insert the thumbdrive into an open USB port and check to make sure FreeBSD detects its using the command below.

# dmesg | tail

USB drives are handled by the SCSI subsystem so look for output that would resemble the following:

ugen0.3: <vendor 0x0d7d> at usbus0
umass0: <vendor 0x0d7d USB DISK 2.0, class 0/0, rev 2.00/0.50, addr 3> on usbus0
umass0:  SCSI over Bulk-Only; quirks = 0xc180
umass0:2:0:-1: Attached to scbus2
da1 at umass-sim0 bus 0 scbus2 target 0 lun 0
da1: < USB DISK 2.0 1.16> Removable Direct Access SCSI device
da1: Serial Number 073A0C251C0B
da1: 1.000MB/s transfers
da1: 124MB (253952 512 byte sectors: 64H 32S/T 124C)
da1: quirks=0x3<NO_SYNC_CACHE,NO_6_BYTE>

From the output above, we know the device is /dev/da1.  You can also query the SCSI system to see if the device is found. Note this output also shows “da1”.

root@bsdbox:~ # camcontrol devlist
<WDC WD400EB-00CPF0 06.04G06>      at scbus0 target 0 lun 0 (pass0,ada0)
<IOMEGA ZIP 100 12.A>              at scbus0 target 1 lun 0 (pass1,da0)
<LG CD-ROM CRD-8400B 1.03>         at scbus1 target 0 lun 0 (cd0,pass2)
<SONY CD-RW  CRX220E1 6YS1>        at scbus1 target 1 lun 0 (cd1,pass3)
< USB DISK 2.0 1.16>               at scbus2 target 0 lun 0 (da1,pass4)
Trying to mount just /dev/da1 is not going to succeed. We have to tell FreeBSD which partition to mount. Luckily, for most USB thumbdrives without any encryption (such as IronKeys), there is just one partition on the drive. As such, we can specify /dev/da1s1 to get slice #1, or the first partition. To mount the partition manually, we can use the mount(8) command while specifying the filesystem on the device. The USB thumbdrive I have is FAT formatted, so the “-t msdosfs” option to the mount command will tell FreeBSD to override the default format of UFS. Before running the mount command, however, make sure you have a place to mount the filesystem. I happened to choose /media/usb.
root@bsdbox:~ # mkdir /media/usb
root@bsdbox:~ # mount -t msdosfs /dev/da1s1 /media/usb
The mount command should complete silently, and you should be able to access /media/usb and copy files to and from the device. When you have finished with the device and want to remove it, use the umount command.
root@bsdbox:~ # umount /media/usb
It is safe to remove the device now without potentially damaging the filesystem on the device. After physically unplugging the device from the USB port, if you go back and look at the messages on the system (dmesg | tail), you will see something like the following:
ugen0.3: <vendor 0x0d7d> at usbus0 (disconnected)
umass0: at uhub0, port 2, addr 3 (disconnected)
da1 at umass-sim0 bus 0 scbus2 target 0 lun 0
da1: < USB DISK 2.0 1.16> s/n 073A0C251C0B detached
(da1:umass-sim0:0:0:0): Periph destroyed

Homebrew versus Consumer Routers

Arstechnica has an interesting article on typical consumer home routers versus a pfsense configuration running on a mini-computer. The article is very interesting to me as I have been thinking about doing something similar such as having a router/firewall on wired device and setting up separate access points with different hardware. The comments are also very interesting, making mention of SOHO networking equipment from Ubiquiti Networks or MikroTik. I love the idea of putting up Ubiquiti UniFi APs on each floor of my house, and having them feed into a router with PoE. However, I would need to figure out how to run Ethernet cable up three floors in this townhouse. I suppose I could try to run it up behind the drywall in the staircase, and install one AP between the first and second floor, and the other AP in the basement. However, I don’t know that there would be an easy way to run the Ethernet cable. I would probably have to drill through some 2x4s. It would be a fun and interesting project too…one to put on the TODO list I guess!

USB Endpoints in Linux

USB endpoints are the logical entities that make up one-way communication channels in a USB system.  Endpoints are unidirectional and the direction is always with respect to the host.  IN endpoints are for transfers to the host from the device (host<-device), and OUT endpoints are for transfers from the host to the device (host->device).  Refer to the USB specification for more information about endpoints, or try this reference from BeyondLogic on endpoint types.

In the Linux kernel the struct usb_host_endpoint, defined in <linux/usb.h>, is used to describe a USB endpoint.

struct usb_host_endpoint {
   struct usb_endpoint_descriptor    desc;
   struct usb_ss_ep_comp_descriptor  ss_ep_comp;
   struct list_head    urb_list;
   void        *hcpriv;
   struct ep_device    *ep_dev;  /* For sysfs info */
   unsigned char *extra;   /* Extra descriptors */
   int extralen;
   int enabled;
};

Among other things, it contains the head of a linked list of URBs (to be discussed later) and a struct usb_endpoint_descriptorwhich is used to describe the endpoint in terms of the USB spec.  In <linux/usb/ch9.h>, the definition of the endpoint descriptor can be seen:

struct usb_endpoint_descriptor {
   __u8  bLength;
   __u8  bDescriptorType;
   __u8  bEndpointAddress;
   __u8  bmAttributes;
   __le16 wMaxPacketSize;
   __u8  bInterval;
   /* NOTE:  these two are _only_ in audio endpoints. */
   /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
   __u8  bRefresh;
   __u8  bSynchAddress;
} __attribute__ ((packed));

In order to determine the direction of the endpoint, a few helper functions are available:

#define USB_ENDPOINT_DIR_MASK   0x80
...
static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)
{
  return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);
}
...
static inline int usb_endpoint_dir_out(
        const struct usb_endpoint_descriptor *epd)
{
  return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
}

This could all be done manually with the USB_ENDPOINT_DIR_MASK mask itself, but in order to guard against futures changes, it is best to use the abstractions and helper functions defined in the kernel.  The endpoint transfer type can also be retrieved with helper functions.

#define USB_ENDPOINT_XFERTYPE_MASK  0x03
...
static inline int usb_endpoint_type(const struct usb_endpoint_descriptor *epd)
{
  return epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
}
...
static inline int usb_endpoint_xfer_bulk(const struct usb_endpoint_descriptor *epd)
{
  return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
    USB_ENDPOINT_XFER_BULK);
}
...
static inline int usb_endpoint_xfer_control(const struct usb_endpoint_descriptor *epd)
{
  return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
    USB_ENDPOINT_XFER_CONTROL);
}
...
static inline int usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd)
{
  return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
    USB_ENDPOINT_XFER_INT);
}
...
static inline int usb_endpoint_xfer_isoc(const struct usb_endpoint_descriptor *epd)
{
  return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
    USB_ENDPOINT_XFER_ISOC);
}

Next the Linux kernel representations of USB Interfaces and USB Configurations will be examined.