diff --git a/smartmontools/CHANGELOG b/smartmontools/CHANGELOG
index 4ae0848862245a4f466bb0783d3664692063ef16..9cbea76cd1be53ea2ee48e29b8e5b538e69ee223 100644
--- a/smartmontools/CHANGELOG
+++ b/smartmontools/CHANGELOG
@@ -43,6 +43,8 @@ NOTES FOR FUTURE RELEASES: see TODO file.
 
 <DEVELOPERS: ADDITIONS TO THE CHANGE LOG GO JUST BELOW HERE, PLEASE>
 
+  [AS] FreeBSD: added support for the ada disks, based on agapon patch
+
   [CF] Add names for attributes 184 and 188, see ticket #17.
 
   [CF] configure.in: Change configure date syntax.
diff --git a/smartmontools/os_freebsd.cpp b/smartmontools/os_freebsd.cpp
index b433648200089d2b6cf16ef6918e848eba6220ad..e5ce4ec3fa34991346905ad26b5345f89e3065ba 100644
--- a/smartmontools/os_freebsd.cpp
+++ b/smartmontools/os_freebsd.cpp
@@ -67,11 +67,12 @@
 #define CONTROLLER_UNKNOWN              0x00
 #define CONTROLLER_ATA                  0x01
 #define CONTROLLER_SCSI                 0x02
-#define CONTROLLER_3WARE_678K           0x04  // NOT set by guess_device_type()
-#define CONTROLLER_3WARE_9000_CHAR      0x05  // set by guess_device_type()
-#define CONTROLLER_3WARE_678K_CHAR      0x06  // set by guess_device_type()
-#define CONTROLLER_HPT                  0x09  // SATA drives behind HighPoint Raid controllers
-#define CONTROLLER_CCISS  0x10  // CCISS controller 
+#define CONTROLLER_3WARE_678K           0x03  // NOT set by guess_device_type()
+#define CONTROLLER_3WARE_9000_CHAR      0x04  // set by guess_device_type()
+#define CONTROLLER_3WARE_678K_CHAR      0x05  // set by guess_device_type()
+#define CONTROLLER_HPT                  0x06  // SATA drives behind HighPoint Raid controllers
+#define CONTROLLER_CCISS                0x07  // CCISS controller 
+#define CONTROLLER_ATACAM               0x08
 
 static __unused const char *filenameandversion="$Id$";
 
@@ -315,6 +316,7 @@ static int get_ata_channel_unit ( const char* name, int* unit, int* dev) {
 // osst, nosst and sg.
 static const char * fbsd_dev_prefix = _PATH_DEV;
 static const char * fbsd_dev_ata_disk_prefix = "ad";
+static const char * fbsd_dev_atacam_disk_prefix = "ada";
 static const char * fbsd_dev_scsi_disk_plus = "da";
 static const char * fbsd_dev_scsi_pass = "pass";
 static const char * fbsd_dev_scsi_tape1 = "sa";
@@ -332,6 +334,7 @@ int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *chan,
   // No Autodetection if device type was specified by user
   if (*type){
     if(!strcmp(type,"ata")) return CONTROLLER_ATA;
+    if(!strcmp(type,"atacam")) return CONTROLLER_ATACAM;
     if(!strcmp(type,"cciss")) return CONTROLLER_CCISS;
     if(!strcmp(type,"scsi") || !strcmp(type,"sat")) goto handlescsi;
     if(!strcmp(type,"3ware")){
@@ -354,6 +357,13 @@ int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *chan,
     // else advance pointer to following characters
     dev_name += dev_prefix_len;
   }
+
+  // form /dev/ada* or ada*
+  if (!strncmp(fbsd_dev_atacam_disk_prefix, dev_name,
+               strlen(fbsd_dev_atacam_disk_prefix))) {
+    return CONTROLLER_ATACAM;
+  }
+
   // form /dev/ad* or ad*
   if (!strncmp(fbsd_dev_ata_disk_prefix, dev_name,
     strlen(fbsd_dev_ata_disk_prefix))) {
@@ -488,6 +498,14 @@ bool freebsd_smart_device::open()
       return false;
     }
   }
+  if (parse_ok == CONTROLLER_ATACAM) {
+    if ((fdchan->camdev = ::cam_open_device(dev,O_RDWR)) == NULL) {
+      perror("cam_open_device");
+      free(fdchan);
+      errno = ENOENT;
+      return false;
+    }
+  }
 
   if (parse_ok == CONTROLLER_3WARE_678K_CHAR) {
     char buf[512];
@@ -575,6 +593,9 @@ bool freebsd_smart_device::close()
     failed=::close(fdchan->atacommand);
 #endif
 
+  if (fdchan->camdev != NULL)
+    cam_close_device(fdchan->camdev);
+
   // if close succeeded, then remove from device list
   // Eduard, should we also remove it from list if close() fails?  I'm
   // not sure. Here I only remove it from list if close() worked.
@@ -602,6 +623,10 @@ public:
 
 protected:
   virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+  #ifdef IOCATAREQUEST
+	virtual int do_cmd(struct freebsd_dev_channel* con, struct ata_ioc_request* request);
+  #endif
 };
 
 freebsd_ata_device::freebsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
@@ -610,6 +635,72 @@ freebsd_ata_device::freebsd_ata_device(smart_interface * intf, const char * dev_
 {
 }
 
+int freebsd_ata_device::do_cmd(struct freebsd_dev_channel* con, struct ata_ioc_request* request)
+{
+  return ioctl(con->device, IOCATAREQUEST, request);
+}
+
+#if __FreeBSD_version > 800100
+class freebsd_atacam_device : public freebsd_ata_device
+{
+public:
+  freebsd_atacam_device(smart_interface * intf, const char * dev_name, const char * req_type)
+  : smart_device(intf, dev_name, "atacam", req_type), freebsd_ata_device(intf, dev_name, req_type)
+  {}
+
+protected:
+	virtual int do_cmd(struct freebsd_dev_channel* con, struct ata_ioc_request* request);
+};
+
+int freebsd_atacam_device::do_cmd(struct freebsd_dev_channel* con, struct ata_ioc_request* request)
+{
+  union ccb ccb;
+  int camflags;
+
+  memset(&ccb, 0, sizeof(ccb));
+
+  if (request->count == 0)
+    camflags = CAM_DIR_NONE;
+  else if (request->flags == ATA_CMD_READ)
+    camflags = CAM_DIR_IN;
+  else
+    camflags = CAM_DIR_OUT;
+
+  cam_fill_ataio(&ccb.ataio,
+                 0,
+                 NULL,
+                 camflags,
+                 MSG_SIMPLE_Q_TAG,
+                 (u_int8_t*)request->data,
+                 request->count,
+                 request->timeout);
+
+  // ata_28bit_cmd
+  ccb.ataio.cmd.flags = 0;
+  ccb.ataio.cmd.command = request->u.ata.command;
+  ccb.ataio.cmd.features = request->u.ata.feature;
+  ccb.ataio.cmd.lba_low = request->u.ata.lba;
+  ccb.ataio.cmd.lba_mid = request->u.ata.lba >> 8;
+  ccb.ataio.cmd.lba_high = request->u.ata.lba >> 16;
+  ccb.ataio.cmd.device = 0x40 | ((request->u.ata.lba >> 24) & 0x0f);
+  ccb.ataio.cmd.sector_count = request->u.ata.count;
+
+  ccb.ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+  if (cam_send_ccb(con->camdev, &ccb) < 0) {
+    err(1, "cam_send_ccb");
+    return -1;
+  }
+
+  if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
+    return 0;
+
+  cam_error_print(con->camdev, &ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr);
+  return -1;
+}
+
+#endif
+
 int freebsd_ata_device::ata_command_interface(smart_command_set command, int select, char * data)
 {
  int fd=get_fd();
@@ -754,7 +845,7 @@ int freebsd_ata_device::ata_command_interface(smart_command_set command, int sel
   unsigned char low,high;
 
 #ifdef IOCATAREQUEST
-  if ((retval=ioctl(con->device, IOCATAREQUEST, &request)) || request.error)
+  if ((retval=do_cmd(con, &request)) || request.error)
 #else
   if ((retval=ioctl(con->atacommand, IOCATA, &iocmd)) || request.error)
 #endif
@@ -790,7 +881,7 @@ int freebsd_ata_device::ata_command_interface(smart_command_set command, int sel
  }
 
 #ifdef IOCATAREQUEST
- if ((retval=ioctl(con->device, IOCATAREQUEST, &request)) || request.error)
+ if ((retval=do_cmd(con, &request)) || request.error)
 #else
  if ((retval=ioctl(con->atacommand, IOCATA, &iocmd)) || request.error)
 #endif
@@ -1583,6 +1674,8 @@ public:
 protected:
   virtual ata_device * get_ata_device(const char * name, const char * type);
 
+  virtual ata_device * get_atacam_device(const char * name, const char * type);
+
   virtual scsi_device * get_scsi_device(const char * name, const char * type);
 
   virtual smart_device * autodetect_smart_device(const char * name);
@@ -1614,6 +1707,11 @@ ata_device * freebsd_smart_interface::get_ata_device(const char * name, const ch
   return new freebsd_ata_device(this, name, type);
 }
 
+ata_device * freebsd_smart_interface::get_atacam_device(const char * name, const char * type)
+{
+  return new freebsd_atacam_device(this, name, type);
+}
+
 scsi_device * freebsd_smart_interface::get_scsi_device(const char * name, const char * type)
 {
   return new freebsd_scsi_device(this, name, type);
@@ -2176,6 +2274,8 @@ smart_device * freebsd_smart_interface::autodetect_smart_device(const char * nam
   switch (guess) {
   case CONTROLLER_ATA : 
     return new freebsd_ata_device(this, name, "");
+  case CONTROLLER_ATACAM : 
+    return new freebsd_atacam_device(this, name, "");
   case CONTROLLER_SCSI: 
     // Try to detect possible USB->(S)ATA bridge
     if (get_usb_id(name, vendor_id, product_id, version)) {
diff --git a/smartmontools/os_freebsd.h b/smartmontools/os_freebsd.h
index 9a59afb71221debfd6e026458f93bb20a8d7af05..f3f17626ca2f1bc0935dc68dd63daa71b3fd1a17 100644
--- a/smartmontools/os_freebsd.h
+++ b/smartmontools/os_freebsd.h
@@ -92,6 +92,7 @@ struct freebsd_dev_channel {
 #endif
   char* devname;                // the SCSI device name
   int   unitnum;                // the SCSI unit number
+  struct cam_device *camdev;
 };
 
 #define FREEBSD_MAXDEV 64