[Libburn] libburn_drive_get_busy get's locked

Tiago Cogumbreiro cogumbreiro@linus.uac.pt
Thu, 04 Dec 2003 01:26:14 -0100


--=-gF5Ce/NaMz0iG8kaNFps
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

I am testing the changes to making possible to get the progress while
blanking the media, unfornatly after i call libburn_drive_get_busy it
locks until i the cd is fully blanked thus making impossible to test the
progress, did i do anything wrong?
I am sending as attachments the changed source files.


--=-gF5Ce/NaMz0iG8kaNFps
Content-Disposition: attachment; filename=blank.c
Content-Type: text/x-c; name=blank.c; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */

#include "libburn.h"
#include "toc.h"

#include <unistd.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>

static struct libburn_drive_info *drives;
static unsigned int n_drives;

static void blank_disc(struct drive *drive)
{
	enum libburn_drive_status s;
	struct libburn_progress progress;

	if (libburn_drive_grab(drive, 1) != LIBBURN_GRAB_OK) {
		fprintf(stderr, "Unable to open the drive!\n");
		return;
	}

	while (libburn_drive_get_busy(drive, NULL))
		usleep(1000);

	while ((s = libburn_drive_get_status(drive)) == LIBBURN_STATUS_UNREADY)
		usleep(1000);
	printf("%d\n", s);
	if (s != LIBBURN_STATUS_FULL) {
		libburn_drive_release(drive, 0);
		fprintf(stderr, "No disc found!\n");
		return;
	}

	fprintf(stderr, "Blanking disc...\n");
	libburn_erase_disc(drive, 1);
	fprintf(stderr, "please wait...\n");

	while (libburn_drive_get_busy(drive, &progress)) {
		fprintf(stderr, "%d\n", progress.abs_sector);
		usleep(200);
	}
	fprintf(stderr, "Done\n");

	libburn_drive_release(drive, 0);
}

void parse_args(int argc, char **argv, int *drive)
{
	int i;
	int help = 0;

	for (i = 1; i < argc; ++i) {
		if (!strcmp(argv[i], "--drive")) {
			++i;
			if (i >= argc)
				printf("--drive requires an argument\n");
			else
				*drive = atoi(argv[i]);
		} else if (!strcmp(argv[i], "--verbose")) {
			++i;
			if (i >= argc)
				printf("--verbose requires an argument\n");
			else
				libburn_set_verbosity(atoi(argv[i]));
		} else if (!strcmp(argv[i], "--help")) {
			help = 1;
		}
	}
	if (help) {
		printf("Usage: %s [--drive <num>] [--verbose <level>]\n", argv[0]);
		exit(EXIT_FAILURE);
	}
}

int main(int argc, char **argv)
{
	int drive = 0;

	parse_args(argc, argv, &drive);

	fprintf(stderr, "Initializing library...");
	if (libburn_initialize())
		fprintf(stderr, "Success\n");
	else {
		printf("Failed\n");
		return 1;
	}

	fprintf(stderr, "Scanning for devices...");
	while (!libburn_drive_scan(&drives, &n_drives)) ;
	fprintf(stderr, "Done\n");

	blank_disc(drives[drive].drive);

	libburn_finish();
	return 0;
}

--=-gF5Ce/NaMz0iG8kaNFps
Content-Disposition: attachment; filename=sg.c
Content-Type: text/x-c; name=sg.c; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */

#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <malloc.h>
#include <string.h>
#include <sys/poll.h>
#include <linux/hdreg.h>

#include "transport.h"
#include "drive.h"
#include "sg.h"
#include "spc.h"
#include "mmc.h"
#include "sbc.h"
#include "debug.h"
#include "toc.h"
#include "util.h"

static void enumerate_common(char *fname);

static int sgio_test(int fd)
{
	unsigned char test_ops[] = {0,0,0,0,0,0};
	sg_io_hdr_t s;
	memset(&s, 0, sizeof(sg_io_hdr_t));
	s.interface_id = 'S';
	s.dxfer_direction = SG_DXFER_NONE;
	s.cmd_len = 6;
	s.cmdp = test_ops;
	s.timeout = 12345;
	return ioctl(fd, SG_IO, &s);
}

void ata_enumerate(void)
{
	struct hd_driveid tm;
	int i, fd;
	char fname[10];

	for (i = 0; i < 26; i++) {
		sprintf(fname, "/dev/hd%c", 'a' + i);
		fd = open(fname, O_RDONLY | O_NONBLOCK);
		if (fd == -1)
			continue;
 
		/* found a drive */
		ioctl(fd, HDIO_GET_IDENTITY, &tm);

		/* not atapi */
		if (!(tm.config & 0x8000) || (tm.config & 0x4000)) {
			close(fd);
			continue;
		}

		/* if SG_IO fails on an atapi device, we should stop 
		   trying to use hd* devices */
		if (sgio_test(fd) == -1) {
			close(fd);
			return;
		}
		close(fd);
		enumerate_common(fname);
	}
}

void sg_enumerate(void)
{
	struct sg_scsi_id sid;
	int i, fd;
	char fname[10];

	for (i = 0; i < 32; i++) {
		sprintf(fname, "/dev/sg%d", i);
		fd = open(fname, O_RDONLY);
		if (fd == -1)
			continue;

		/* found a drive */
		ioctl(fd, SG_GET_SCSI_ID, &sid);
		close(fd);
		if (sid.scsi_type != TYPE_ROM)
			continue;

		enumerate_common(fname);
	}
}

static void enumerate_common(char *fname)
{
	struct drive *t;
	struct drive out;

	out.devname = libburn_strdup(fname);
	out.fd = -1337;

	out.atomic_grab = sg_atomic_grab;
	out.release = sg_release;
	out.issue_command = sg_issue_command;
	out.getcaps = spc_getcaps;
	out.released = 1;
	out.status = LIBBURN_STATUS_UNREADY;

	out.eject = sbc_eject;
	out.load = sbc_load;
	out.lock = spc_prevent;
	out.unlock = spc_allow;
	out.read_disc_info = spc_sense_write_params;
	out.read_toc = mmc_read_toc;
	out.write = mmc_write;
	out.erase = mmc_erase;
	out.read_sectors = mmc_read_sectors;
	out.perform_opc = mmc_perform_opc;
	out.set_speed = mmc_set_speed;
	out.send_parameters = spc_select_error_params;
	out.send_write_parameters = spc_select_write_params;
	out.sync_cache = mmc_sync_cache;
	out.request_sense = mmc_request_sense;

	out.idata = malloc(sizeof(struct scsi_inquiry_data));
	out.idata->valid = 0;
	out.mdata = malloc(sizeof(struct scsi_mode_data));
	out.mdata->valid = 0;
	memset(&out.params, 0, sizeof(struct params));
	t = libburn_drive_register(&out);

/* try to get the drive info */
	if (sg_atomic_grab(t)) {
		libburn_print(2, "getting drive info\n");
		t->getcaps(t);
		t->unlock(t);
		t->released = 1;
	} else {
		libburn_print(2, "unable to grab new located drive\n");
	}

}

/*
	we use the sg reference count to decide whether we can use the
	drive or not.
	if refcount is not one, drive is open somewhere else.
*/
int sg_atomic_grab(struct drive *d)
{
	int fd, count;

	fd = open(d->devname, O_RDWR | O_NONBLOCK);
	assert(fd != -1337);
	if (-1 != fd) {
/*		er = ioctl(fd, SG_GET_ACCESS_COUNT, &count);*/
count = 1;
		if (1 == count) {
			d->fd = fd;
			fcntl(fd, F_SETOWN, getpid());
			d->released = 0;
			return 1;
		}
		libburn_print(1, "could not acquire drive - already open\n");
		close(fd);
		return 0;
	}
	libburn_print(1, "could not acquire drive\n");
	return 0;
}

/*
	non zero return means you still have the drive and it's not
	in a state to be released? (is that even possible?)
*/

int sg_release(struct drive *d)
{
	if (d->fd < 1) {
		libburn_print(1, "release an ungrabbed drive.  die\n");
		return 0;
	}
	close(d->fd);
	d->fd = -1337;
	return 0;
}

int sg_issue_command(struct drive *d, struct command *c)
{
	int done = 0;
	int err;
	sg_io_hdr_t s;

	c->error = 0;

	if (d->fd < 1 || d->released) {
		libburn_print(1,
			      "command issued on ungrabbed drive, chaos.\n");
		libburn_print(1, "fd = %d, released = %d\n", d->fd,
			      d->released);
	}
	memset(&s, 0, sizeof(sg_io_hdr_t));

	s.interface_id = 'S';

	if (c->dir == TO_DRIVE)
		s.dxfer_direction = SG_DXFER_TO_DEV;
	else if (c->dir == FROM_DRIVE)
		s.dxfer_direction = SG_DXFER_FROM_DEV;
	else if (c->dir == NO_TRANSFER) {
		s.dxfer_direction = SG_DXFER_NONE;
		assert(!c->page);
	}
	s.cmd_len = c->oplen;
	s.cmdp = c->opcode;
	s.mx_sb_len = 32;
	s.sbp = c->sense;
	memset(c->sense, 0, sizeof(c->sense));
	s.timeout = 20000000;
	if (c->page) {
		s.dxferp = c->page->data;
		if (c->dir == FROM_DRIVE) {
			s.dxfer_len = BUFFER_SIZE;
/* touch page so we can use valgrind */
			memset(c->page->data, 0, BUFFER_SIZE);
		} else {
			assert(c->page->bytes > 0);
			s.dxfer_len = c->page->bytes;
		}
	} else {
		s.dxferp = NULL;
		s.dxfer_len = 0;
	}
	s.usr_ptr = c;

	do {
		err = ioctl(d->fd, SG_IO, &s);
		assert(err != -1);
		if (s.sb_len_wr) {
			switch(scsi_error(d, s.sbp, s.sb_len_wr)) {
			case RETRY:
				done = 0;
				break;
			case FAIL:
				done = 1;
				c->error = 1;
				break;
			}
		} else {
			done = 1;
		}
	} while (!done);
	return 1;
}

enum response scsi_error(struct drive *d, unsigned char *sense, int senselen)
{
	int key, asc, ascq;

	senselen = senselen;
	key = sense[2];
	asc = sense[12];
	ascq = sense[13];

	libburn_print(12, "CONDITION: 0x%x 0x%x 0x%x on %s %s\n",
		      key, asc, ascq, d->idata->vendor, d->idata->product);

	switch (asc) {
	case 0:
		libburn_print(12, "NO ERROR!\n");
		return RETRY;

	case 2:
		libburn_print(1, "not ready\n");
		return RETRY;
	case 4:
		libburn_print(1,
			      "logical unit is in the process of becoming ready\n");
		return RETRY;
	case 0x24:
		if (key == 5)
			libburn_print(1, "invalid field in cdb\n");
		else
			break;
		return FAIL;
	case 0x21:
		libburn_print(1, "invalid address or something\n");
		return FAIL;
	case 0x28:
		if (key == 6)
			libburn_print(1,
				      "Not ready to ready change, medium may have changed\n");
		else
			break;
		return RETRY;
	case 0x20:
		if (key == 5)
			libburn_print(1, "bad opcode\n");
		return FAIL;
	case 0x3A:
		libburn_print(12, "Medium not present in %s %s\n",
			      d->idata->vendor, d->idata->product);

		d->status = LIBBURN_STATUS_NO_DISC;
		return FAIL;
	}
	libburn_print(1, "unknown failure\n");
	libburn_print(1, "key:0x%x, asc:0x%x, ascq:0x%x\n", key, asc, ascq);
	return FAIL;
}

--=-gF5Ce/NaMz0iG8kaNFps
Content-Disposition: attachment; filename=mmc.c
Content-Type: text/x-c; name=mmc.c; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "libburn.h"
#include "transport.h"
#include "mmc.h"
#include "spc.h"
#include "drive.h"
#include "debug.h"
#include "toc.h"
#include "structure.h"

static unsigned char MMC_GET_TOC[] = { 0x43, 2, 2, 0, 0, 0, 0, 16, 0, 0 };
static unsigned char MMC_GET_ATIP[] = { 0x43, 2, 4, 0, 0, 0, 0, 16, 0, 0 };
static unsigned char MMC_GET_DISC_INFO[] = { 0x51, 0, 0, 0, 0, 0, 0, 16, 0, 0 };
static unsigned char MMC_READ_CD[] = { 0xBE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static unsigned char MMC_ERASE[] = { 0xA1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static unsigned char MMC_SEND_OPC[] = { 0x54, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static unsigned char MMC_SET_SPEED[] ={ 0xBB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static unsigned char MMC_WRITE_12[] = { 0xAA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static unsigned char MMC_WRITE_10[] = { 0x2A, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static unsigned char MMC_GET_CONFIGURATION[] ={ 0x46, 0, 0, 0, 0, 0, 16, 0, 0 };
static unsigned char MMC_SYNC_CACHE[] = { 0x35, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static unsigned char MMC_GET_EVENT[] = { 0x4A, 1, 0, 0, 16, 0, 0, 0, 8, 0 };
static unsigned char MMC_REQUEST_SENSE[] = { 0x03, 0, 0, 0, 18, 0};

void mmc_request_sense(struct drive *d, struct buffer *buf)
{
	struct command c;
	c.oplen = sizeof(MMC_REQUEST_SENSE);
	memcpy(c.opcode, MMC_REQUEST_SENSE, sizeof(MMC_REQUEST_SENSE));
	c.page = buf;
	c.page->sectors = 0;
	c.page->bytes = 0;
	c.dir = FROM_DRIVE;
	d->issue_command(d, &c);
}

void mmc_get_event(struct drive *d)
{
	struct buffer buf;
	struct command c;
	c.oplen = sizeof(MMC_GET_EVENT);
	memcpy(c.opcode, MMC_GET_EVENT, sizeof(MMC_GET_EVENT));
	c.page = &buf;
	c.page->bytes = 0;
	c.page->sectors = 0;
	c.dir = FROM_DRIVE;
	d->issue_command(d, &c);
	printf("0x%x:0x%x:0x%x:0x%x\n", 
c.page->data[0], c.page->data[1], c.page->data[2], c.page->data[3]);
	printf("event: %d:%d:%d:%d\n",
c.page->data[4], c.page->data[5], c.page->data[6], c.page->data[7]);
}

void mmc_write_12(struct drive *d, int start, struct buffer *buf)
{
	struct command c;
	int len;

	len = buf->sectors;
	assert(buf->bytes >= buf->sectors);	/* can be == at 0... */
	libburn_print(100, "trying to write %d at %d\n", len, start);
	memcpy(c.opcode, MMC_WRITE_12, sizeof(MMC_WRITE_12));
	c.oplen = sizeof(MMC_WRITE_12);
	c.opcode[2] = start >> 24;
	c.opcode[3] = (start >> 16) & 0xFF;
	c.opcode[4] = (start >> 8) & 0xFF;
	c.opcode[5] = start & 0xFF;
	c.opcode[6] = len >> 24;
	c.opcode[7] = (len >> 16) & 0xFF;
	c.opcode[8] = (len >> 8) & 0xFF;
	c.opcode[9] = len & 0xFF;
	c.page = buf;
	c.dir = TO_DRIVE;

	d->issue_command(d, &c);
}

void mmc_write(struct drive *d, int start, struct buffer *buf)
{
	struct command c;
	int len;

	len = buf->sectors;
	assert(buf->bytes >= buf->sectors);	/* can be == at 0... */
	libburn_print(100, "trying to write %d at %d\n", len, start);
	memcpy(c.opcode, MMC_WRITE_10, sizeof(MMC_WRITE_10));
	c.oplen = sizeof(MMC_WRITE_10);
	c.opcode[2] = start >> 24;
	c.opcode[3] = (start >> 16) & 0xFF;
	c.opcode[4] = (start >> 8) & 0xFF;
	c.opcode[5] = start & 0xFF;
	c.opcode[6] = 0;
	c.opcode[7] = (len >> 8) & 0xFF;
	c.opcode[8] = len & 0xFF;
	c.page = buf;
	c.dir = TO_DRIVE;
/*
	printf("%d, %d, %d, %d - ", c->opcode[2], c->opcode[3], c->opcode[4], c->opcode[5]);
	printf("%d, %d, %d, %d\n", c->opcode[6], c->opcode[7], c->opcode[8], c->opcode[9]);
*/

/*	write(fileno(stderr), c.page->data, c.page->bytes);*/

	d->issue_command(d, &c);
}

void mmc_read_toc(struct drive *d)
{
/* read full toc, all sessions, in m/s/f form, 4k buffer */
	struct track *track;
	struct session *session;
	struct buffer buf;
	struct command c;
	int dlen;
	int i;
	unsigned char *tdata;

	memcpy(c.opcode, MMC_GET_TOC, sizeof(MMC_GET_TOC));
	c.oplen = sizeof(MMC_GET_TOC);
	c.page = &buf;
	c.page->bytes = 0;
	c.page->sectors = 0;
	c.dir = FROM_DRIVE;
	d->issue_command(d, &c);

	if (c.error) {
		d->busy = LIBBURN_BUSY_NO;
		return;
	}

	dlen = c.page->data[0] * 256 + c.page->data[1];
	d->toc_entries = (dlen - 2) / 11;
/*
	some drives fail this check.

	assert(((dlen - 2) % 11) == 0);
*/
	d->toc_entry = malloc(d->toc_entries * sizeof(struct toc_entry));
	tdata = c.page->data + 4;

	libburn_print(12, "TOC:\n");

	d->disc = libburn_disc_create(); 

	for (i = 0; i < c.page->data[3]; i++) {
		session = libburn_session_create();
		libburn_session_add(d->disc, session, LIBBURN_POS_END);
		libburn_session_destroy(session);
	}
	for (i = 0; i < d->toc_entries; i++, tdata += 11) {
		libburn_print(12, "S %d, PT %d, TNO %d : ", tdata[0], tdata[3],
			      tdata[2]);
		libburn_print(12, "(%d:%d:%d)", tdata[8], tdata[9], tdata[10]);
		libburn_print(12, "A(%d:%d:%d)", tdata[4], tdata[5], tdata[6]);
		libburn_print(12, " - control %d, adr %d\n", tdata[1] & 0xF,
			      tdata[1] >> 4);

		if (tdata[3] == 1) {
			if (libburn_msf_to_lba(tdata[8], tdata[9], tdata[10])) {
				d->disc->session[0]->hidefirst = 1;
				track = libburn_track_create();
				libburn_track_add(
                                            d->disc->session[tdata[0] - 1],
				            track, LIBBURN_POS_END);
				libburn_track_destroy(track);

			}
		}
		if (tdata[3] < 100) {
			track = libburn_track_create();
			libburn_track_add(d->disc->session[tdata[0] - 1],
		   	                  track, LIBBURN_POS_END);
			track->entry = &d->toc_entry[i];
			libburn_track_destroy(track);
		}
		d->toc_entry[i].session = tdata[0];
		d->toc_entry[i].adr = tdata[1] >> 4;
		d->toc_entry[i].control = tdata[1] & 0xF;
		d->toc_entry[i].tno = tdata[2];
		d->toc_entry[i].point = tdata[3];
		d->toc_entry[i].min = tdata[4];
		d->toc_entry[i].sec = tdata[5];
		d->toc_entry[i].frame = tdata[6];
		d->toc_entry[i].zero = tdata[7];
		d->toc_entry[i].pmin = tdata[8];
		d->toc_entry[i].psec = tdata[9];
		d->toc_entry[i].pframe = tdata[10];
		if (tdata[3] == 0xA0)
			d->disc->session[tdata[0] - 1]->firsttrack = tdata[8];
		if (tdata[3] == 0xA1)
			d->disc->session[tdata[0] - 1]->lasttrack = tdata[8];
		if (tdata[3] == 0xA2)
			d->disc->session[tdata[0] - 1]->leadout_entry = 
                                                            &d->toc_entry[i];
	}
	d->status = LIBBURN_STATUS_FULL;
	toc_find_modes(d);
}

void mmc_read_disc_info(struct drive *d)
{
	struct buffer buf;
	unsigned char *data;
	struct command c;

	memcpy(c.opcode, MMC_GET_DISC_INFO, sizeof(MMC_GET_DISC_INFO));
	c.oplen = sizeof(MMC_GET_DISC_INFO);
	c.page = &buf;
	c.page->sectors = 0;
	c.page->bytes = 0;
	c.dir = FROM_DRIVE;
	d->issue_command(d, &c);

	if (c.error) {
		d->busy = LIBBURN_BUSY_NO;
		return;
	}

	data = c.page->data;

	switch (data[2] & 3) {
	case 0:
		d->toc_entries = 0;
		d->start_lba = libburn_msf_to_lba(data[17], data[18], data[19]);
		d->end_lba = libburn_msf_to_lba(data[21], data[22], data[23]);
		d->status = LIBBURN_STATUS_BLANK;
		spc_try_write_modes(d);
		break;
	case 1:
	case 2:
		mmc_read_toc(d);
		break;
	}
}

void mmc_read_atip(struct drive *d)
{
	struct buffer buf;
	struct command c;

	memcpy(c.opcode, MMC_GET_ATIP, sizeof(MMC_GET_ATIP));
	c.oplen = sizeof(MMC_GET_ATIP);
	c.page = &buf;
	c.page->bytes = 0;
	c.page->sectors = 0;

	c.dir = FROM_DRIVE;
	d->issue_command(d, &c);
	libburn_print(1, "atip shit for you\n");
}

void mmc_read_sectors(struct drive *d,
		      int start,
		      int len,
                      const struct libburn_read_opts *o,
		      struct buffer *buf)
{
	int temp;
	int errorblock, req;
	struct command c;

	assert(len >= 0);
/* if the drive isn't busy, why the hell are we here? */
	assert(d->busy);
printf("reading %d from %d\n", len, start);
	memcpy(c.opcode, MMC_READ_CD, sizeof(MMC_READ_CD));
	c.oplen = sizeof(MMC_READ_CD);
	temp = start;
	c.opcode[5] = temp & 0xFF;
	temp >>= 8;
	c.opcode[4] = temp & 0xFF;
	temp >>= 8;
	c.opcode[3] = temp & 0xFF;
	temp >>= 8;
	c.opcode[2] = temp & 0xFF;
	c.opcode[8] = len & 0xFF;
	len >>= 8;
	c.opcode[7] = len & 0xFF;
	len >>= 8;
	c.opcode[6] = len & 0xFF;
	req = 0xF8;
	if (d->busy == LIBBURN_BUSY_GRABBING ||
	    o->protected_audio ||
	    o->report_recovered_errors)
		req |= 2;

	c.opcode[10] = 0;
/* always read the subcode, throw it away later, since we don't know
   what we're really reading
*/
	if (d->busy == LIBBURN_BUSY_GRABBING || (o->subcodes_audio)
	    || (o->subcodes_data))
		c.opcode[10] = 1;

	c.opcode[9] = req;
	c.page = buf;
	c.dir = FROM_DRIVE;
	d->issue_command(d, &c);

	if (c.error) {
printf("got an error over here\n");
printf("%d, %d, %d, %d\n", c.sense[3], c.sense[4], c.sense[5], c.sense[6]);
		errorblock = (c.sense[3] << 24) +
			(c.sense[4] << 16) + (c.sense[5] << 8) + c.sense[6];
		c.page->sectors = errorblock - start + 1;
		libburn_print(1, "error on block %d\n", errorblock);
		printf("error on block %d\n", errorblock);
		printf("returning %d sectors\n", c.page->sectors);
	}
}

void mmc_erase(struct drive *d, int fast)
{
	struct command c;

	memcpy(c.opcode, MMC_ERASE, sizeof(MMC_ERASE));

	c.opcode[1] = !!fast;
	c.oplen = sizeof(MMC_ERASE);
	c.page = NULL;
	c.dir = NO_TRANSFER;
	d->issue_command(d, &c);
}

void mmc_read_lead_in(struct drive *d, struct buffer *buf)
{
	int len;
	struct command c;

	len = buf->sectors;
	memcpy(c.opcode, MMC_READ_CD, sizeof(MMC_READ_CD));
	c.oplen = sizeof(MMC_READ_CD);
	c.opcode[5] = 0;
	c.opcode[4] = 0;
	c.opcode[3] = 0;
	c.opcode[2] = 0xF0;
	c.opcode[8] = 1;
	c.opcode[7] = 0;
	c.opcode[6] = 0;
	c.opcode[9] = 0;
	c.opcode[10] = 2;
	c.page = buf;
	c.dir = FROM_DRIVE;
	d->issue_command(d, &c);
}

void mmc_perform_opc(struct drive *d)
{
	struct command c;

	memcpy(c.opcode, MMC_SEND_OPC, sizeof(MMC_SEND_OPC));
	c.oplen = sizeof(MMC_SEND_OPC);
	c.opcode[1] = 1;
	c.page = NULL;
	c.dir = NO_TRANSFER;
	d->issue_command(d, &c);
}

void mmc_set_speed(struct drive *d, int read, int write)
{
	struct command c;

	memcpy(c.opcode, MMC_SET_SPEED, sizeof(MMC_SET_SPEED));
	c.oplen = sizeof(MMC_SET_SPEED);
	c.opcode[2] = read >> 8;
	c.opcode[3] = read & 0xFF;
	c.opcode[4] = write >> 8;
	c.opcode[5] = write & 0xFF;
	c.page = NULL;
	c.dir = NO_TRANSFER;
	d->issue_command(d, &c);
}

void mmc_get_configuration(struct drive *d)
{
	struct buffer buf;
	int len;
	struct command c;

	memcpy(c.opcode, MMC_GET_CONFIGURATION,
	       sizeof(MMC_GET_CONFIGURATION));
	c.oplen = sizeof(MMC_GET_CONFIGURATION);
	c.page = &buf;
	c.page->sectors = 0;
	c.page->bytes = 0;
	c.dir = FROM_DRIVE;
	d->issue_command(d, &c);

	libburn_print(1, "got it back\n");
	len = (c.page->data[0] << 24)
		+ (c.page->data[1] << 16)
		+ (c.page->data[2] << 8)
		+ c.page->data[3];
	libburn_print(1, "all %d bytes of it\n", len);
	libburn_print(1, "%d, %d, %d, %d\n",
		      c.page->data[0],
		      c.page->data[1], c.page->data[2], c.page->data[3]);
}

void mmc_sync_cache(struct drive *d)
{
	struct command c;

	memcpy(c.opcode, MMC_SYNC_CACHE, sizeof(MMC_SYNC_CACHE));
	c.oplen = sizeof(MMC_SYNC_CACHE);
	c.page = NULL;
	c.dir = NO_TRANSFER;
	d->issue_command(d, &c);
}

--=-gF5Ce/NaMz0iG8kaNFps
Content-Disposition: attachment; filename=mmc.h
Content-Type: text/x-c-header; name=mmc.h; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */

#ifndef __MMC
#define __MMC

struct drive;
struct command;
struct buffer;

/* MMC commands */

void mmc_read(struct drive *);

void mmc_get_event(struct drive *);
void mmc_write(struct drive *, int start, struct buffer *buf);
void mmc_sync_cache(struct drive *);
void mmc_load(struct drive *);
void mmc_eject(struct drive *);
void mmc_erase(struct drive *, int);
void mmc_read_toc(struct drive *);
void mmc_read_disc_info(struct drive *);
void mmc_read_atip(struct drive *);
void mmc_read_sectors(struct drive *,
		      int,
		      int,
                      const struct libburn_read_opts *,
		      struct buffer *);
void mmc_set_speed(struct drive *, int, int);
void mmc_read_lead_in(struct drive *, struct buffer *);
void mmc_perform_opc(struct drive *);
void mmc_get_configuration(struct drive *);
void mmc_request_sense(struct drive *d, struct buffer *buf);

#endif /*__MMC*/

--=-gF5Ce/NaMz0iG8kaNFps
Content-Disposition: attachment; filename=drive.c
Content-Type: text/x-c; name=drive.c; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */

#include <malloc.h>
#include <unistd.h>
#include <signal.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "libburn.h"
#include "drive.h"
#include "transport.h"
#include "message.h"
#include "debug.h"
#include "init.h"
#include "toc.h"
#include "util.h"
#include "sg.h"
#include "structure.h"

static struct drive drive_array[255];
static int drivetop = -1;

void libburn_drive_destroy(void)
{
	int i;
	struct drive *d;

	for (i = 0; i < drivetop + 1; i++) {
		d = &drive_array[i];
		free((void *)d->idata);
		free((void *)d->mdata);
		free((void *)d->toc_entry);
		free(d->devname);
	}
	drivetop = -1;
	memset(drive_array, 0, sizeof(drive_array));
}

/*
void drive_read_lead_in(int dnum)
{
	mmc_read_lead_in(&drive_array[dnum], get_4k());
}
*/
unsigned int libburn_drive_count(void)
{
	return drivetop + 1;
}

enum libburn_grab libburn_drive_grab(struct drive *d, int le)
{
	int errcode;

	if (!d->released) {
		libburn_print(1, "can't grab - already grabbed\n");
		return LIBBURN_GRAB_FAIL;
	}
	errcode = d->atomic_grab(d);

	if (errcode == 0) {
		libburn_print(1, "low level drive grab failed\n");
		return LIBBURN_GRAB_FAIL;
	}
	d->busy = LIBBURN_BUSY_GRABBING;

	if (le)
		d->load(d);

	d->lock(d);
	if (d->mdata->cdr_write || d->mdata->cdrw_write ||
	    d->mdata->dvdr_write || d->mdata->dvdram_write) {
		d->read_disc_info(d);
	} else
		d->read_toc(d);
	d->busy = LIBBURN_BUSY_NO;
	return LIBBURN_GRAB_OK;
}

struct drive *libburn_drive_register(struct drive *d)
{
	d->write_type = -1;
	d->block_type = -1;
	d->block_types[0] = 0;
	d->block_types[1] = 0;
	d->block_types[2] = 0;
	d->block_types[3] = 0;
	d->toc_temp = 0;
	d->nwa = 0;
	d->alba = 0;
	d->rlba = 0;
	d->cancel = 0;
	d->busy = LIBBURN_BUSY_NO;
	d->params.flags = 0;
	d->toc_entries = 0;
	d->toc_entry = NULL;
/* remove mediacatalog from drive plz */
	d->has_mediacatalog = 0;
	d->disc = NULL;
	memcpy(&drive_array[drivetop + 1], d, sizeof(struct drive));
	return &drive_array[++drivetop];
}

void libburn_drive_release(struct drive *d, int le)
{
	if (d->released)
		libburn_print(1, "second release on drive!\n");
	assert(!d->busy);
	if (le)
		d->eject(d);
	d->unlock(d);

	d->release(d);

	d->status = LIBBURN_STATUS_UNREADY;
	d->released = 1;
	if (d->toc_entry)
		free(d->toc_entry);
	d->toc_entry = NULL;
	d->toc_entries = 0;
	if (d->disc != NULL) {
		libburn_disc_destroy(d->disc);
		d->disc = NULL;
	}
/* XXX remove mediacatalog from drive*/
	d->has_mediacatalog = 0;
}

void libburn_wait_all()
{
	unsigned int i;
	int finished = 0;
	struct drive *d;

	while (!finished) {
		finished = 1;
		d = drive_array;
		for (i = libburn_drive_count(); i > 0; --i, ++d) {
			assert(d->released);
		}
		if (!finished)
			sleep(1);
	}
}

void libburn_erase_disc_sync(struct drive *d, int fast)
{
	libburn_message_clear_queue();

	libburn_print(1, "erasing drive %s %s\n", d->idata->vendor,
		      d->idata->product);

	if (d->status != LIBBURN_STATUS_FULL)
		return;

	d->cancel = 0;
	d->busy = LIBBURN_BUSY_ERASING;
	d->erase(d, fast);
	d->busy = LIBBURN_BUSY_NO;
}

enum libburn_drive_status libburn_drive_get_status(struct drive *d)
{
	assert(!d->released);
	return d->status;
}

int libburn_drive_get_busy(struct drive *d, struct libburn_progress *p)
{
	if (p) {
		if(d->busy == LIBBURN_BUSY_ERASING) {
			struct buffer *buf = malloc(sizeof(struct buffer));
			p->session = 1;
			p->sessions = 1;
			p->track = 1;
			p->tracks = 1;
			p->index = 1;
			p->indices = 1;
			p->abs_sectors = 0x10000;
			p->rel_sectors = 0x10000;
			d->request_sense(d, buf);
			p->abs_sector = (buf->data[17] << 8) | buf->data[18];
			p->rel_sector = p->abs_sector;
			free(buf);
		}
		/* this is where we do stuff! */
	}
	return d->busy != LIBBURN_BUSY_NO;
}

void libburn_drive_cancel(struct drive *d)
{
	d->cancel = 1;
}

int libburn_drive_get_block_types(struct drive *d,
				  enum libburn_write_types write_type)
{
	libburn_print(12, "write type: %d\n", write_type);
	assert(			/* (write_type >= LIBBURN_WRITE_PACKET) && */
		      (write_type <= LIBBURN_WRITE_RAW));
	return d->block_types[write_type];
}

static void strip_spaces(char *str)
{
	char *tmp;

	tmp = str + strlen(str) - 1;
	while (isspace(*tmp))
		*(tmp--) = '\0';

	tmp = str;
	while (*tmp) {
		if (isspace(*tmp) && isspace(*(tmp + 1))) {
			char *tmp2;

			for (tmp2 = tmp + 1; *tmp2; ++tmp2)
				*(tmp2 - 1) = *tmp2;
			*(tmp2 - 1) = '\0';
		} else
			++tmp;
	}
}

static int drive_getcaps(struct drive *d, struct libburn_drive_info *out)
{
	struct scsi_inquiry_data *id;

	assert(d->idata);
	assert(d->mdata);
	if (!d->idata->valid || !d->mdata->valid)
		return 0;

	id = (struct scsi_inquiry_data *)d->idata;

	memcpy(out->vendor, id->vendor, sizeof(id->vendor));
	strip_spaces(out->vendor);
	memcpy(out->product, id->product, sizeof(id->product));
	strip_spaces(out->product);
	strncpy(out->location, d->devname, 16);
	out->location[16] = '\0';
	out->buffer_size = d->mdata->buffer_size;
	out->read_dvdram = !!d->mdata->dvdram_read;
	out->read_dvdr = !!d->mdata->dvdr_read;
	out->read_dvdrom = !!d->mdata->dvdrom_read;
	out->read_cdr = !!d->mdata->cdr_read;
	out->read_cdrw = !!d->mdata->cdrw_read;
	out->write_dvdram = !!d->mdata->dvdram_write;
	out->write_dvdr = !!d->mdata->dvdr_write;
	out->write_cdr = !!d->mdata->cdr_write;
	out->write_cdrw = !!d->mdata->cdrw_write;
	out->write_simulate = !!d->mdata->simulate;
	out->read_max_speed = d->mdata->max_read_speed;
	out->write_max_speed = d->mdata->max_write_speed;
	out->c2_errors = !!d->mdata->c2_pointers;
	out->drive = d;
	return 1;
}

int libburn_drive_scan_sync(struct libburn_drive_info *drives[],
			    unsigned int *n_drives)
{
	/* state vars for the scan process */
	static int scanning = 0, scanned, found;
	static unsigned num_scanned, count;
	unsigned int i;
	struct drive *d;

	assert(libburn_running);

	if (!scanning) {
		scanning = 1;
		/* make sure the drives aren't in use */
		libburn_wait_all();	/* make sure the queue cleans up
					   before checking for the released
					   state */

		d = drive_array;
		count = libburn_drive_count();
		for (i = 0; i < count; ++i, ++d)
			assert(d->released == 1);

		/* refresh the lib's drives */
		sg_enumerate();
		ata_enumerate();
		count = libburn_drive_count();
		if (count)
			*drives =
				malloc(sizeof(struct libburn_drive_info) *
				       count);
		else
			*drives = NULL;
		*n_drives = scanned = found = num_scanned = 0;
	}

	for (i = 0; i < count; ++i) {
		if (scanned & (1 << i))
			continue;	/* already scanned the device */

		while (!drive_getcaps(&drive_array[i],
				      &(*drives)[num_scanned])) {
			sleep(1);
		}
		scanned |= 1 << i;
		found |= 1 << i;
		num_scanned++;
		(*n_drives)++;
	}

	if (num_scanned == count) {
		/* done scanning */
		scanning = 0;
		return 1;
	}
	return 0;
}

struct disc *libburn_drive_get_disc(struct drive *d)
{
	d->disc->refcnt++;
	return d->disc;
}

--=-gF5Ce/NaMz0iG8kaNFps
Content-Disposition: attachment; filename=drive.h
Content-Type: text/x-c-header; name=drive.h; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */

#ifndef __DRIVE
#define __DRIVE

#include "libburn.h"
#include "toc.h"
#include "structure.h"

struct drive;
struct command;
struct mempage;

#define LEAD_IN 1
#define GAP 2
#define USER_DATA 3
#define LEAD_OUT 4
#define SYNC 5

/** Possible busy states for a drive */
enum libburn_drive_busy
{
	/** The drive is not in an operation */
	LIBBURN_BUSY_NO,
	/** The library is spawning the processes to handle a pending
	    operation (A read/write/etc is about to start but hasn't quite
	    yet) */
	LIBBURN_BUSY_SPAWNING,
	/** The drive is reading data from a disc */
	LIBBURN_BUSY_READING,
	/** The drive is writing data to a disc */
	LIBBURN_BUSY_WRITING,
	/** The drive is erasing a disc */
	LIBBURN_BUSY_ERASING,
	/** The drive is being grabbed */
	LIBBURN_BUSY_GRABBING
};

#define SESSION_LEADOUT_ENTRY(d,s) (d)->toc->session[(s)].leadout_entry

#define CURRENT_SESSION_START(d) \
	libburn_msf_to_lba(d->toc->session[d->currsession].start_m, \
	                   d->toc->session[d->currsession].start_s, \
	                   d->toc->session[d->currsession].start_f)

#define SESSION_END(d,s) \
	TOC_ENTRY_PLBA((d)->toc, SESSION_LEADOUT_ENTRY((d), (s)))

#define PREVIOUS_SESSION_END(d) \
	TOC_ENTRY_PLBA((d)->toc, SESSION_LEADOUT_ENTRY((d), (d)->currsession-1))

#define LAST_SESSION_END(d) \
	TOC_ENTRY_PLBA((d)->toc, \
	               SESSION_LEADOUT_ENTRY((d), (d)->toc->sessions-1))

struct drive *libburn_drive_register(struct drive *);

unsigned int libburn_drive_count(void);
void libburn_wait_all();
void libburn_drive_release_finish(struct drive *d);
int libburn_sector_length_write(struct drive *d);
int libburn_track_control(struct drive *d, int);
void libburn_write_empty_sector(int fd);
void libburn_write_empty_subcode(int fd);
void libburn_drive_destroy(void);
void libburn_send_toc(struct drive *d, struct toc *t);

int libburn_drive_scan_sync(struct libburn_drive_info *drives[],
			    unsigned int *n_drives);
void libburn_erase_disc_sync(struct drive *d, int fast);

#endif /* __DRIVE */

--=-gF5Ce/NaMz0iG8kaNFps--