From jylefort@brutele.be  Tue Nov  8 01:31:23 2005
Return-Path: <jylefort@brutele.be>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id E2FB616A41F;
	Tue,  8 Nov 2005 01:31:23 +0000 (GMT)
	(envelope-from jylefort@brutele.be)
Received: from 212.68.244.220.brutele.be (212.68.244.220.brutele.be [212.68.244.220])
	by mx1.FreeBSD.org (Postfix) with ESMTP id C519143D45;
	Tue,  8 Nov 2005 01:31:22 +0000 (GMT)
	(envelope-from jylefort@brutele.be)
Received: from jsite.lefort.net (jsite.lefort.net [192.168.1.2])
	by gateway.lefort.net (Postfix) with ESMTP id F073854FB;
	Tue,  8 Nov 2005 02:31:20 +0100 (CET)
Received: by jsite.lefort.net (Postfix, from userid 1000)
	id DB8C9C0FF; Tue,  8 Nov 2005 02:31:20 +0100 (CET)
Message-Id: <20051108013120.DB8C9C0FF@jsite.lefort.net>
Date: Tue,  8 Nov 2005 02:31:20 +0100 (CET)
From: Jean-Yves Lefort <jylefort@FreeBSD.org>
Reply-To: Jean-Yves Lefort <jylefort@FreeBSD.org>
To: FreeBSD-gnats-submit@freebsd.org
Cc: sos@FreeBSD.org
Subject: Add idle and standby commands to atacontrol
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         88634
>Category:       bin
>Synopsis:       [patch] add idle and standby commands to atacontrol(8)
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    sos
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Tue Nov 08 01:40:10 GMT 2005
>Closed-Date:    Wed Jan 04 21:45:37 GMT 2006
>Last-Modified:  Thu Jan  5 08:40:03 GMT 2006
>Originator:     Jean-Yves Lefort
>Release:        FreeBSD 6.0-RELEASE i386
>Organization:
>Environment:
System: FreeBSD jsite.lefort.net 6.0-RELEASE FreeBSD 6.0-RELEASE #0: Mon Nov 7 19:32:08 CET 2005 jylefort@jsite.lefort.net:/usr/obj/usr/src/sys/JSITE i386
>Description:
Inspired by sysutils/ataidle. Timer calculation taken from NetBSD's atactl.
>How-To-Repeat:
>Fix:
--- atacontrol.8.orig	Fri Aug 19 17:54:42 2005
+++ atacontrol.8	Tue Nov  8 01:51:32 2005
@@ -64,6 +64,12 @@
 .Ic mode
 .Ar device
 .Nm
+.Ic idle
+.Ar device Oo Ar seconds Oc
+.Nm
+.Ic standby
+.Ar device Oo Ar seconds Oc
+.Nm
 .Ic info
 .Ar channel
 .Nm
@@ -187,6 +193,20 @@
 (alias
 .Cm UDMA133 ) .
 The device name and manufacture/version strings are shown.
+.It Ic idle
+Place
+.Ar device
+into idle mode. Additionally, if
+.Ar seconds
+are specified, set the idle timer to that number of seconds. A value of
+0 will disable the idle timer.
+.It Ic standby
+Place
+.Ar device
+into standby mode. Additionally, if
+.Ar seconds
+are specified, set the standby timer to that number of seconds. A value of
+0 will disable the standby timer.
 .It Ic cap
 Show detailed info about the device on
 .Ar device .
--- atacontrol.c.orig	Sun Aug  7 13:16:58 2005
+++ atacontrol.c	Tue Nov  8 02:02:51 2005
@@ -37,6 +37,7 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <limits.h>
 #include <string.h>
 #include <sysexits.h>
 
@@ -48,6 +49,8 @@
 void cap_print(struct ata_params *parm);
 int ata_cap_print(int fd);
 int info_print(int fd, int channel, int prchan);
+int open_device(const char *name);
+void set_power(int fd, const char *seconds, u_int8_t immediate_command, u_int8_t set_command);
 
 const char *
 mode2str(int mode)
@@ -108,6 +111,8 @@
 		"        atacontrol rebuild array\n"
 		"        atacontrol status array\n"
 		"        atacontrol mode device [mode]\n"
+		"        atacontrol idle device [seconds]\n"
+		"        atacontrol standby device [seconds]\n"
 		"        atacontrol cap device\n"
 	);
 	exit(EX_USAGE);
@@ -287,6 +292,68 @@
 }
 
 int
+open_device(const char *name)
+{
+	int disk;
+	int fd;
+	char device[64];
+
+	if (!(sscanf(name, "ad%d", &disk) == 1 ||
+	      sscanf(name, "acd%d", &disk) == 1 ||
+	      sscanf(name, "afd%d", &disk) == 1 ||
+	      sscanf(name, "ast%d", &disk) == 1)) {
+		fprintf(stderr, "atacontrol: Invalid device %s\n",
+			name);
+		exit(EX_USAGE);
+	}
+
+	sprintf(device, "/dev/%s", name);
+	if ((fd = open(device, O_RDONLY)) < 0)
+		err(1, "device not found");
+
+	return fd;
+}
+
+void
+set_power(int fd, const char *seconds, u_int8_t immediate_command, u_int8_t set_command)
+{
+	struct ata_ioc_request request;
+
+	memset(&request, 0, sizeof(request));
+	request.flags = ATA_CMD_CONTROL;
+	request.timeout = 1000;
+	if (seconds) {
+		unsigned long idle;
+		char *end;
+
+		idle = strtoul(seconds, &end, 0);
+
+		if (*end != '\0') {
+			fprintf(stderr, "atacontrol: Invalid idle time %s\n", seconds);
+                        exit(EX_USAGE);
+		}
+		if (idle > 19800) {
+			fprintf(stderr, "atacontrol: Idle time has a maximum value of 5.5 hours\n");
+                        exit(EX_USAGE);
+		}
+		if (idle != 0 && idle < 5) {
+			fprintf(stderr, "atacontrol: Idle time must be at least 5 seconds\n");
+                        exit(EX_USAGE);
+		}
+
+		request.u.ata.command = set_command;
+		if (idle <= 240 * 5)
+			request.u.ata.count = idle / 5;
+		else
+			request.u.ata.count = idle / (30 * 60) + 240;
+	} else
+		request.u.ata.command = immediate_command;
+
+	if (ioctl(fd, IOCATAREQUEST, &request) < 0)
+		err(1, "ioctl(IOCATAREQUEST)");
+}
+
+int
 main(int argc, char **argv)
 {
 	int fd;
@@ -295,20 +362,8 @@
 		usage();
 
 	if (!strcmp(argv[1], "mode") && (argc == 3 || argc == 4)) {
-		int disk, mode;
-		char device[64];
-
-		if (!(sscanf(argv[2], "ad%d", &disk) == 1 ||
-		      sscanf(argv[2], "acd%d", &disk) == 1 ||
-		      sscanf(argv[2], "afd%d", &disk) == 1 ||
-		      sscanf(argv[2], "ast%d", &disk) == 1)) {
-			fprintf(stderr, "atacontrol: Invalid device %s\n",
-				argv[2]);
-			exit(EX_USAGE);
-		}
-		sprintf(device, "/dev/%s", argv[2]);
-		if ((fd = open(device, O_RDONLY)) < 0)
-			err(1, "device not found");
+		int mode;
+		fd = open_device(argv[2]);
 		if (argc == 4) {
 			mode = str2mode(argv[3]);
 			if (ioctl(fd, IOCATASMODE, &mode) < 0)
@@ -321,21 +376,20 @@
 		}
 		exit(EX_OK);
 	}
+	if (!strcmp(argv[1], "idle") && (argc == 3 || argc == 4)) {
+		fd = open_device(argv[2]);
+		set_power(fd, argc == 4 ? argv[3] : NULL,
+			  ATA_IDLE_IMMEDIATE, ATA_IDLE_CMD);
+		exit(EX_OK);
+	}
+	if (!strcmp(argv[1], "standby") && (argc == 3 || argc == 4)) {
+		fd = open_device(argv[2]);
+		set_power(fd, argc == 4 ? argv[3] : NULL,
+			  ATA_STANDBY_IMMEDIATE, ATA_STANDBY_CMD);
+		exit(EX_OK);
+	}
 	if (!strcmp(argv[1], "cap") && argc == 3) {
-		int disk;
-		char device[64];
-
-		if (!(sscanf(argv[2], "ad%d", &disk) == 1 ||
-		      sscanf(argv[2], "acd%d", &disk) == 1 ||
-		      sscanf(argv[2], "afd%d", &disk) == 1 ||
-		      sscanf(argv[2], "ast%d", &disk) == 1)) {
-			fprintf(stderr, "atacontrol: Invalid device %s\n",
-				argv[2]);
-			exit(EX_USAGE);
-		}
-		sprintf(device, "/dev/%s", argv[2]);
-		if ((fd = open(device, O_RDONLY)) < 0)
-			err(1, "device not found");
+		fd = open_device(argv[2]);
 		ata_cap_print(fd);
 		exit(EX_OK);
 	}
>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->sos 
Responsible-Changed-By: rodrigc 
Responsible-Changed-When: Tue Nov 8 01:47:41 GMT 2005 
Responsible-Changed-Why:  
Over to maintainer 

http://www.freebsd.org/cgi/query-pr.cgi?pr=88634 

From: Jean-Yves Lefort <jylefort@FreeBSD.org>
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/88634: [patch] add idle and standby commands to
 atacontrol(8)
Date: Wed, 9 Nov 2005 15:43:29 +0100

 And add checkpower command (once again, NetBSD's atactl was used as a
 reference):
 
 --- atacontrol.8.orig	Fri Aug 19 17:54:42 2005
 +++ atacontrol.8	Wed Nov  9 15:34:49 2005
 @@ -64,6 +64,15 @@
  .Ic mode
  .Ar device
  .Nm
 +.Ic idle
 +.Ar device Oo Ar seconds Oc
 +.Nm
 +.Ic standby
 +.Ar device Oo Ar seconds Oc
 +.Nm
 +.Ic checkpower
 +.Ar device
 +.Nm
  .Ic info
  .Ar channel
  .Nm
 @@ -187,6 +196,23 @@
  (alias
  .Cm UDMA133 ) .
  The device name and manufacture/version strings are shown.
 +.It Ic idle
 +Place
 +.Ar device
 +into idle mode. Additionally, if
 +.Ar seconds
 +are specified, set the idle timer to that number of seconds. A value of
 +0 will disable the idle timer.
 +.It Ic standby
 +Place
 +.Ar device
 +into standby mode. Additionally, if
 +.Ar seconds
 +are specified, set the standby timer to that number of seconds. A value of
 +0 will disable the standby timer.
 +.It Ic checkpower
 +Report the current power status (active, idle or standby) of
 +.Ar device .
  .It Ic cap
  Show detailed info about the device on
  .Ar device .
 --- atacontrol.c.orig	Sun Aug  7 13:16:58 2005
 +++ atacontrol.c	Wed Nov  9 15:28:21 2005
 @@ -37,9 +37,13 @@
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
 +#include <limits.h>
  #include <string.h>
  #include <sysexits.h>
  
 +/* FIXME: this should go in sys/sys/ata.h */
 +#define ATA_CHECK_POWER			0xe5
 +
  const char *mode2str(int mode);
  int str2mode(char *str);
  void usage(void);
 @@ -48,6 +52,9 @@
  void cap_print(struct ata_params *parm);
  int ata_cap_print(int fd);
  int info_print(int fd, int channel, int prchan);
 +int open_device(const char *name);
 +void control_request(int fd, struct ata_ioc_request *request, u_int8_t command, u_int16_t count);
 +void set_power(int fd, const char *seconds, u_int8_t immediate_command, u_int8_t set_command);
  
  const char *
  mode2str(int mode)
 @@ -108,6 +115,9 @@
  		"        atacontrol rebuild array\n"
  		"        atacontrol status array\n"
  		"        atacontrol mode device [mode]\n"
 +		"        atacontrol idle device [seconds]\n"
 +		"        atacontrol standby device [seconds]\n"
 +		"        atacontrol checkpower device\n"
  		"        atacontrol cap device\n"
  	);
  	exit(EX_USAGE);
 @@ -287,6 +297,86 @@
  }
  
  int
 +open_device(const char *name)
 +{
 +	int disk;
 +	int fd;
 +	char device[64];
 +
 +	if (!(sscanf(name, "ad%d", &disk) == 1 ||
 +	      sscanf(name, "acd%d", &disk) == 1 ||
 +	      sscanf(name, "afd%d", &disk) == 1 ||
 +	      sscanf(name, "ast%d", &disk) == 1)) {
 +		fprintf(stderr, "atacontrol: Invalid device %s\n",
 +			name);
 +		exit(EX_USAGE);
 +	}
 +
 +	sprintf(device, "/dev/%s", name);
 +	if ((fd = open(device, O_RDONLY)) < 0)
 +		err(1, "device not found");
 +
 +	return fd;
 +}
 +
 +void
 +control_request(int fd, struct ata_ioc_request *request, u_int8_t command, u_int16_t count)
 +{
 +	memset(request, 0, sizeof(*request));
 +	request->flags = ATA_CMD_CONTROL;
 +	request->timeout = 1000;
 +	request->u.ata.command = command;
 +	request->u.ata.count = count;
 +
 +	if (ioctl(fd, IOCATAREQUEST, request) < 0)
 +		err(1, "ioctl(IOCATAREQUEST)");
 +
 +	/*
 +	 * FIXME: check request->error (need to add ATA error code
 +	 * definitions to ata.h).
 +	 */
 +}
 +
 +void
 +set_power(int fd, const char *seconds, u_int8_t immediate_command, u_int8_t set_command)
 +{
 +	struct ata_ioc_request request;
 +	u_int8_t command;
 +	u_int16_t count;
 +
 +	if (seconds) {
 +		unsigned long idle;
 +		char *end;
 +
 +		idle = strtoul(seconds, &end, 0);
 +
 +		if (*end != '\0') {
 +			fprintf(stderr, "atacontrol: Invalid idle time %s\n", seconds);
 +			exit(EX_USAGE);
 +		}
 +		if (idle > 19800) {
 +			fprintf(stderr, "atacontrol: Idle time has a maximum value of 5.5 hours\n");
 +			exit(EX_USAGE);
 +		}
 +		if (idle != 0 && idle < 5) {
 +			fprintf(stderr, "atacontrol: Idle time must be at least 5 seconds\n");
 +			exit(EX_USAGE);
 +		}
 +
 +		command = set_command;
 +		if (idle <= 240 * 5)
 +			count = idle / 5;
 +		else
 +			count = idle / (30 * 60) + 240;
 +	} else {
 +		command = immediate_command;
 +		count = 0;
 +	}
 +
 +	control_request(fd, &request, command, count);
 +}
 +
 +int
  main(int argc, char **argv)
  {
  	int fd;
 @@ -295,20 +385,8 @@
  		usage();
  
  	if (!strcmp(argv[1], "mode") && (argc == 3 || argc == 4)) {
 -		int disk, mode;
 -		char device[64];
 -
 -		if (!(sscanf(argv[2], "ad%d", &disk) == 1 ||
 -		      sscanf(argv[2], "acd%d", &disk) == 1 ||
 -		      sscanf(argv[2], "afd%d", &disk) == 1 ||
 -		      sscanf(argv[2], "ast%d", &disk) == 1)) {
 -			fprintf(stderr, "atacontrol: Invalid device %s\n",
 -				argv[2]);
 -			exit(EX_USAGE);
 -		}
 -		sprintf(device, "/dev/%s", argv[2]);
 -		if ((fd = open(device, O_RDONLY)) < 0)
 -			err(1, "device not found");
 +		int mode;
 +		fd = open_device(argv[2]);
  		if (argc == 4) {
  			mode = str2mode(argv[3]);
  			if (ioctl(fd, IOCATASMODE, &mode) < 0)
 @@ -321,21 +399,44 @@
  		}
  		exit(EX_OK);
  	}
 -	if (!strcmp(argv[1], "cap") && argc == 3) {
 -		int disk;
 -		char device[64];
 +	if (!strcmp(argv[1], "idle") && (argc == 3 || argc == 4)) {
 +		fd = open_device(argv[2]);
 +		set_power(fd, argc == 4 ? argv[3] : NULL,
 +			  ATA_IDLE_IMMEDIATE, ATA_IDLE_CMD);
 +		exit(EX_OK);
 +	}
 +	if (!strcmp(argv[1], "standby") && (argc == 3 || argc == 4)) {
 +		fd = open_device(argv[2]);
 +		set_power(fd, argc == 4 ? argv[3] : NULL,
 +			  ATA_STANDBY_IMMEDIATE, ATA_STANDBY_CMD);
 +		exit(EX_OK);
 +	}
 +	if (!strcmp(argv[1], "checkpower") && argc == 3) {
 +		struct ata_ioc_request request;
  
 -		if (!(sscanf(argv[2], "ad%d", &disk) == 1 ||
 -		      sscanf(argv[2], "acd%d", &disk) == 1 ||
 -		      sscanf(argv[2], "afd%d", &disk) == 1 ||
 -		      sscanf(argv[2], "ast%d", &disk) == 1)) {
 -			fprintf(stderr, "atacontrol: Invalid device %s\n",
 -				argv[2]);
 -			exit(EX_USAGE);
 +		fd = open_device(argv[2]);
 +		control_request(fd, &request, ATA_CHECK_POWER, 0);
 +
 +		printf("Current %s power status: ", argv[2]);
 +
 +		switch (request.u.ata.count) {
 +		case 0x00:
 +			printf("standby mode\n");
 +			break;
 +		case 0x80:
 +			printf("idle mode\n");
 +			break;
 +		case 0xff:
 +			printf("active mode\n");
 +			break;
 +		default:
 +			printf("unknown power code (%02x)\n", request.u.ata.count);
  		}
 -		sprintf(device, "/dev/%s", argv[2]);
 -		if ((fd = open(device, O_RDONLY)) < 0)
 -			err(1, "device not found");
 +
 +		exit(EX_OK);
 +	}
 +	if (!strcmp(argv[1], "cap") && argc == 3) {
 +		fd = open_device(argv[2]);
  		ata_cap_print(fd);
  		exit(EX_OK);
  	}
 
 -- 
 Jean-Yves Lefort
 
 jylefort@FreeBSD.org
 http://lefort.be.eu.org/
State-Changed-From-To: open->closed 
State-Changed-By: sos 
State-Changed-When: Wed Jan 4 21:44:40 UTC 2006 
State-Changed-Why:  
Proper idle/spindown support needs to be done on the driver level so 
ATA kan keep track of devices status. 
Its on my TODO list, but its long :) 

http://www.freebsd.org/cgi/query-pr.cgi?pr=88634 

From: Jean-Yves Lefort <jylefort@FreeBSD.org>
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/88634: [patch] add idle and standby commands to
 atacontrol(8)
Date: Thu, 5 Jan 2006 09:30:08 +0100

 You must be talking about sleep mode? As far as I know, idle and
 standby mode do not require driver support, since the drive
 automatically spins up when it receives a request.
 
 -- 
 Jean-Yves Lefort
 
 jylefort@FreeBSD.org
 http://lefort.be.eu.org/
>Unformatted:
