Запись дисков CD-R/RW в Linux. Часть 3::Журнал СА 1.2005
www.samag.ru
Льготная подписка для студентов      
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
О журнале
Журнал «БИТ»
Подписка
Где купить
Авторам
Рекламодателям
Магазин
Архив номеров
Вакансии
Контакты
   

Jobsora


  Опросы

Какие курсы вы бы выбрали для себя?  

Очные
Онлайновые
Платные
Бесплатные
Я и так все знаю

 Читать далее...

1001 и 1 книга  
28.05.2019г.
Просмотров: 2249
Комментарии: 2
Анализ вредоносных программ

 Читать далее...

28.05.2019г.
Просмотров: 2226
Комментарии: 1
Микросервисы и контейнеры Docker

 Читать далее...

28.05.2019г.
Просмотров: 1784
Комментарии: 0
Django 2 в примерах

 Читать далее...

28.05.2019г.
Просмотров: 1309
Комментарии: 0
Введение в анализ алгоритмов

 Читать далее...

27.03.2019г.
Просмотров: 1840
Комментарии: 1
Arduino Uno и Raspberry Pi 3: от схемотехники к интернету вещей

 Читать далее...

Друзья сайта  

Форум системных администраторов  

sysadmins.ru

 Запись дисков CD-R/RW в Linux. Часть 3

Архив номеров / 2005 / Выпуск №1 (26) / Запись дисков CD-R/RW в Linux. Часть 3

Рубрика: Администрирование /  Оборудование

ВЛАДИМИР МЕШКОВ

Запись дисков CD-R/RW в Linux

Часть 3

В третьей части статьи рассматривается порядок использования мультимедийных команд стандарта SCSI (SCSI Multi-Media Commands) для записи многосессионных CD-R/RW дисков в режиме TAO (Track-at-Once).

Работоспособность всех примеров программ была проверена для ОС Linux, ядро 2.4.28. В ядре включены режим SCSI-эмуляции для ATAPI-устройств (SCSI host adapter emulation for IDE ATAPI devices) и поддержка SCSI Generic драйвера. Использовались следующие модели приводов для чтения и записи CD-R/RW и DVD-R/RW дисков:

  • TEAC CD-W524E 1.0E
  • MITSUMI CR-48XATE
  • NEC CD-RW NR-9400A R800
  • ASUS DRW-1604P 1.09

Работа над ошибками

Прежде чем приступить к рассмотрению основного материала, устраним ошибки, допущенные во второй части статьи [5]. Проблема заключается в следующем – при попытке записи информации с использованием привода ASUS программа аварийно завершает выполнение со следующим сообщением:

lba - 320

Sense data: 0x70 0x00 0x02 0x00 0x00 0x00 0x00 0x0e 0x00 0x00

 0x00 0x00 0x04 0x08 0x00 0x00 0x00 0x00 0x00 0x00

 0x00 0x00

Driver status=0x28

Cannot write image.iso

Здесь Sense Key = 0x02, ASC = 0x04, ASCQ = 0x08, что означает LOGICAL UNIT NOT READY, LONG WRITE IN PROGRESS (устройство не готово к работе, выполняется операция длинной записи). Остановка выполнения программы всегда происходит на секторе номер 320. Аналогичным образом ведут себя приводы MITSUMI и _NEC. С приводом TEAC подобной проблемы не возникало. В чем причина этой ошибки? Дело в том, что каждый привод имеет внутренний буфер для данных, и при записи информация сначала попадает в этот буфер, а затем из него переносится на диск. Буфер имеет ограниченную ёмкость, определить которую можно при помощи команды READ BUFFER CAPACITY. Формат этой команды приведён на рис. 1.

READ BUFFER CAPACITY CDB

Bit

Byte

7

6

5

4

3

2

1

0

0

Operation Code (5Ch)

1

Reserved

Reserved

BLOCK

2

Reserved

3

Reserved

4

Reserved

5

Reserved

6

Reserved

7

(MSB)

Allocation Length

8

(LSB)

9

Control

Рисунок 1. Формат команды READ BUFFER CAPACITY

Если поле BLOCK установлено в 0, устройство вернёт блок данных следующего формата (рис. 2):

Buffer Capacity Structure, when Block = 0

Bit

Byte

7

6

5

4

3

2

1

0

0

(MSB)

Data Length

1

(LSB)

2

Reserved

3

Reserved

4

(MSB)

Length of the Buffer

 

(LSB)

5

6

7

8

(MSB)

Available Length of Buffer

 

(LSB)

9

10

11

Рисунок 2. Формат блока данных о буфере устройства, BLOCK = 0

Здесь Length of the Buffer – размер буфера в байтах, Available Length of Buffer – размер неиспользуемой (доступной) области буфера в байтах.

Модифицируем листинг из второй части – при записи информации будем контролировать размер доступной области внутреннего буфера устройства при помощи функции read_buff_cap():

__u32 read_buff_cap()

{

      __u8 read_buff_cap_cmd[10];

      __u8 data_buff[12];

      __u32 buff_alen = 0;

      memset(read_buff_cap_cmd, 0, 10);

      read_buff_cap_cmd[0] = 0x5C;

      read_buff_cap_cmd[8] = 0x0C;

      send_cmd(read_buff_cap_cmd, 10, SG_DXFER_FROM_DEV, data_buff,0x0C, 20);

      memcpy((void *)&buff_alen, data_buff + 8, 4);

      buff_alen = __swab32(buff_alen);

      printf("\tAvailable length - %u\n", buff_alen);

      return buff_alen;

}

Контроль за размером доступной области буфера будет выполняться в функции write_iso, в цикле записи информации (полный листинг этой функции находится в [5]):

int write_iso(__u8 *file_name)

{

      ....

      while(read(in_f, write_buff, CD_FRAMESIZE) > 0) {

      read_buff_cap();

     printf("lba - %6d", lba1);

      lba = __swab32(lba1);

   memcpy((write_cmd + 2), (void *)&lba, 4);

      lba1 += 1;

     send_cmd(write_cmd, 10, SG_DXFER_TO_DEV, write_buff, CD_FRAMESIZE, 20);

      }

      return 0;

}

Результат работы программы (привод ASUS):

      Available length - 1267712

lba -       0      Available length - 1175552

lba -       1      Available length - 1173504

lba -       2      Available length - 1171456

........

lba -       317      Available length - 526336

lba -       318      Available length - 524288

lba -       319      Available length - 0

lba - 320

Sense data: 0x70 0x00 0x02 0x00 0x00 0x00 0x00 0x0e 0x00 0x00

 0x00 0x00 0x04 0x08 0x00 0x00 0x00 0x00 0x00 0x00

 0x00 0x00

Driver status=0x28

Cannot write image.iso

Как только доступная область буфера становится равной нулю, устройство на попытку передачи ему информации отвечает LOGICAL UNIT NOT READY, LONG WRITE IN PROGRESS. Один из вариантов решения данной проблемы – подождать, пока буфер устройства освободится, а затем продолжить запись, например:

while(read_buff_cap() == 0) continue;

Такой вариант устраивает ASUS, но MITSUMI и _NEC так просто не сдаются и, попав в цикл, не хотят его покидать. Спецификация SFF8090i [1] при возникновении такого рода ошибки требует повторить команду записи (см. п.14.48 WRITE(10) command):

While writing is occurring, if WRITE (10) command or WRITE (12) command cannot be terminated immediately due to insufficient buffer

capacity, the logical unit may terminate the WRITE command with CHECK CONDITION status, 2/04/08 LOGICAL UNIT NOT READY, LONG WRITE

IN PROGRESS and the host shall issue the same WRITE command again. After logical unit becomes ready due to sufficient buffer capacity

for the WRITE command, the WRITE command shall be performed normally.

Именно таким образом поступает dvdrecord. Запустив его с ключом -v, мы увидим следующую картину:

Executing "write_g1" command on Bus 0 Target 1,

      Lun 0 timeout 200s

CDB: 2A 00 00 00 3A 01 00 00 1F 00

 

dvdrecord: Input/Output error, write_g1: scsi sendcmd: no error

CDB: 2A 00 00 00 3A 01 00 00 1F 00

 

Status: 0x2 (CHECK CONDITION)

Sense buffer: 70 00 02 00 00 00 00 0E 00 00 00 00

      04 08 00 00

Sense Key: 0x2 Not Ready, Segment 0

Sense Code: 0x04 Qual 0x08 (logical unit not ready,

      Long write in progress) Fru 0x0

Sense flags: BLK 0 (not valid)

cmd finished after 0x000 s timeout 200s

 

Executing "write_g1" command on Bus 0 Target 1,

      Lun 0 timeout 200s

CDB: 2A 00 00 00 3A 01 00 00 1F 00

cmd finished after 0x000 s timeout 200s


CDB – это Command Descriptor Block (дескриптор командного блока), 2A – код команды WRITE_10. Зафиксировав ошибку LONG WRITE IN PROGRESS, программа dvdrecord в соответствии с требованием спецификации повторно посылает устройству команду записи.

Исходя из вышеизложенного, внесём исправления в текст программы для записи ISO-образа на CD-R/RW диск. Изменениям подвергнутся функция посылки пакетной команды устройству send_cmd() и функция записи write_iso:

int send_cmd(__u8 *cmd, __u8 cmdlen, int direction, __u8 *data,  __u32 datalen, unsigned int timeout)

{

      sg_io_hdr_t io_hdr;

      __u8 sense_buffer[32];

#define SK sense_buffer[2]

#define ASC sense_buffer[12]

#define ASCQ sense_buffer[13]

      memset(&io_hdr, 0, sizeof(sg_io_hdr_t));

      io_hdr.interface_id = 'S';

      io_hdr.cmd_len = cmdlen;

      io_hdr.mx_sb_len = sizeof(sense_buffer);

      io_hdr.dxfer_direction = direction;

      io_hdr.dxfer_len = datalen; // размер данных

      io_hdr.dxferp = data; // указатель на блок данных

      io_hdr.cmdp = cmd;

      io_hdr.sbp = sense_buffer;

      io_hdr.timeout = timeout * 1000;

      if(ioctl(sg_fd, SG_IO, &io_hdr) < 0) {

      perror("SG_IO ioctl");

      return -1;

      }

      if((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) {

      if(io_hdr.sb_len_wr > 0) {

/* Если SK/ASC/ASCQ == 02/04/08 (Not Ready. Long Write in Progress), то необходимо повторить команду WRITE_10 */

      if((SK == NOT_READY) && (ASC == 0x04) && (ASCQ == 0x08)) return 1;

      syslog(LOG_INFO,"Command: 0x%02X", io_hdr.cmdp[0]);

syslog(LOG_INFO,"Sense data (SK/ASC/ASCQ): 0x%02X/0x%02X/0x%02X",

      SK,ASC,ASCQ);

      }

if(io_hdr.masked_status)

      syslog(LOG_INFO, "SCSI status = 0x%x\n", io_hdr.status);

      if(io_hdr.host_status)

      syslog(LOG_INFO, "Host status = 0x%x\n", io_hdr.host_status);

      if(io_hdr.driver_status)

      syslog(LOG_INFO, "Driver status = 0x%x\n", io_hdr.driver_status);

      return -1;

      }

      return 0;

}

Текст функции write_iso() целиком приводить не будем, рассмотрим только ключевой фрагмент – посылку устройству командного пакета:

      while(1) {

      ret = send_cmd(write_cmd, 10, SG_DXFER_TO_DEV, write_buff, BUFF_SIZE, 20);

      if(ret == 1) continue; // LONG WRITE IN PROGRESS

      if(ret == 0) break;

      if(ret < 0) return -1; // ошибка записи!

      }

Команда WRITE_10 будет посылаться устройству до тех пор, пока не будет успешно выполнена или не произойдет ошибка, отличная от SK/ASC/ASCQ = 02/04/08 «NOT READY. LONG WRITE IN PROGRESS».

Исходный текст утилиты, выполняющей запись данных на CD-R/RW диск находится по адресу http://bob.netport.com. ua/iso2сd.tar.gz. На этом закончим изучение односессионных CD-дисков и рассмотрим, что нужно сделать для того, чтобы создать многосессионный диск.

Запись многосессионного диска

Для создания многосессионного диска необходимо присвоить полю Multi-session страницы параметров режима записи значение 11b (см. [1, 2]). В этом случае при формировании Lead-In-области указатель B0 таблицы содержания диска (Table of Contents, TOC) будет содержать координаты начала следующей области программ (формат TOC и пример чтения были рассмотрены в [3]). Формирование образа, который будет записан первым, ничем не отличается от формирования образа для односессионного диска. Каждый новый записываемый образ должен содержать ссылку на предыдущий, чтобы драйвер файловой системы мог собрать содержимое всех сессий в единое целое. Для этого при формировании ISO-образа (кроме первого) в опциях утилиты mkisofs необходимо указать стартовые координаты первого трека последней сессии и координаты следующей возможной области программ. Назовем эти координаты A и B. При помощи cdrecord значения A и B определяются следующим образом:

cdrecord -dev=X,Y,Z -msinfo

где X, Y и Z – это координаты устройства (msinfo – это сокращение от multi-session information). После выполнения команды мы получим два числа – искомые стартовые координаты. Эти числа необходимо указать в соответствующих опциях mkisofs (опция -C, см. man mkisofs) при подготовке образа для записи.

Рассмотрим два способа определения значений A и B. Оба способа подразумевают чтение таблицы содержания диска (TOC), только в первом случае мы читаем «сырую» таблицу (RAW TOC), в во втором – информацию о сессиях.

При помощи функции read_raw_toc() прочитаем RAW TOC:

/* Формат записи TOC (см. [1], [2]) */

typedef struct {

      __u8 snum;        // Session Number

      __u8 ctrl  :4;    // Control

      __u8 adr   :4;    // ADR

      __u8 tno;         // TNO (always 0)

      __u8 point; // POINT

      __u8 min;         // AMIN

      __u8 sec;         // ASEC

      __u8 frame; // AFRAME

      __u8 zero;        // 0

      __u8 pmin;        // PMIN

      __u8 psec;        // PSEC

      __u8 pframe;      // PFRAME

} toc_t;

int read_raw_toc()

{

      int i;

      __u8 read_toc_cmd[10];

      __u8 *data_buff; // результаты чтения TOC

      __u16 toc_data_len = 0; // длина записей TOC

      __u32 lba;

      int toc_entries = 0; // число записей TOC

      int last = 0; // номер последней сессии

      toc_t *t;

      data_buff = (__u8 *)malloc(0xFFFF);

      memset(data_buff, 0, 0xFFFF);

      memset(read_toc_cmd, 0, 10);

      read_toc_cmd[0] = READ_TOC;

      read_toc_cmd[2] = 2; // Format Field = 10b - RAW TOC

      read_toc_cmd[7] = 0xFF;

      read_toc_cmd[8] = 0xFF;

      send_cmd(read_toc_cmd, 10, SG_DXFER_FROM_DEV, data_buff, 0xFFFF, 20);

/* Размер TOC */

      *((__u8 *)&toc_data_len) = data_buff[1];

      *((__u8 *)&toc_data_len + 1) = data_buff[0];

/* Число записей TOC */

      toc_entries = (toc_data_len - 2)/11;

/* Число сессий */

      last = data_buff[3];

      printf("Число сессий на диске - %d\n", last);

      t = (toc_t *)malloc(toc_data_len);

      memset((void *)t, 0, toc_data_len);

      memcpy((void *)t, data_buff + 4, toc_data_len);

      free(data_buff);

      printf("Entry\tSession\tPoint\tMin\tSec\tFrame\tPMin\tPsec\tPFrame\tLBA\n");

      for(i = 0; i < toc_entries; i++) {

      printf("%d\t", i);

      printf("%d\t", (t + i)->snum);

      printf("%X\t", (t + i)->point);

      printf("%d\t", (t + i)->min);

      printf("%d\t", (t + i)->sec);

      printf("%d\t", (t + i)->frame);

      printf("%d\t", (t + i)->pmin);

      printf("%d\t", (t + i)->psec);

      printf("%d\t", (t + i)->pframe);

#define PMIN(i) (t + i)->pmin

#define PSEC(i) (t + i)->psec

#define PFRAME(i) (t + i)->pframe

#define MIN(i) (t + i)->min

#define SEC(i) (t + i)->sec

#define FRAME(i) (t + i)->frame

#define POINT(i) (t + i)->point

      if((POINT(i) == 0xC0) || (POINT(i) == 0xC1)) {

        printf("\n");

        continue;

    }

/* Координаты начала следующей возможной области программ содержит указатель B0

 * в полях Min/Sec/Frame. Переводим эти координаты в LBA-формат, с учетом того, что

 * перед треком находится Pre-gap область размером 150 секторов

 */

    if(POINT(i) == 0xB0) lba = MSF2LBA(MIN(i), SEC(i), FRAME(i)) + 150;

      else lba = MSF2LBA(PMIN(i), PSEC(i), PFRAME(i));

           printf("%u\n", lba);

    }

      free(t);

      return 0;

}

Пересчёт координат из MSF-формата в LBA выполняет макрос MSF2LBA следующего вида:

#define MSF2LBA(Min, Sec, Frame) (((Min * 60 + Sec) * 75 + Frame) - 150)

Теперь возьмём диск, на котором записано три сессии, и посмотрим на результат работы функции read_raw_toc():

Число сессий на диске - 3

Entry

Sess

Point

Min

Sec

Frame

PMin

Psec

PFrame

LBA

0

1

A0

0

0

0

1

0

0

4350

1

1

A1

0

0

0

1

0

0

4350

2

1

A2

0

0

0

0

50

18

3618

3

1

1

0

0

0

0

2

0

0

4

1

B0

3

20

18

79

59

74

15018

5

1

C0

102

0

156

97

32

10

 

6

1

C1

72

52

176

0

0

0

 

7

2

A0

0

0

0

2

0

0

8850

8

2

A1

0

0

0

2

0

0

8850

9

2

A2

0

0

0

3

43

35

16610

10

2

2

0

0

0

3

22

18

15018

11

2

B0

5

13

35

79

59

74

23510

12

3

A0

0

0

0

3

0

0

13350

13

3

A1

0

0

0

3

0

0

13350

14

3

A2

0

0

0

5

43

59

25634

15

3

3

0

0

0

5

15

35

23510

16

3

B0

7

13

59

79

59

74

32534

Указатель A0 третьей сессии (запись 12) содержит в поле PMin номер первого трека последней сессии. Это значение равно 3. Запись 15 в полях PMin/PSec/PFrame содержит стартовые координаты этого трека – 5/15/35, или 23510 в LBA-формате. Координаты начала следующей возможной области программ содержит указатель B0 в полях Min/Sec/Frame (запись 16) – 7/13/59 (32534 в LBA-формате).

Второй способ определения значения числа A – чтение информации о сессиях. Для этого в поле Format командного блока READ TOC/PMA/ATIP (см. спецификацию [2], табл. 441) должно принять значение 1. Блок информации о сессиях имеет вид, представленный на рис. 3.

READ TOC/PMA/ATIP response data (Format = 0001b): Multi-session Information

Bit

Byte

7

6

5

4

3

2

1

0

0

(MSB)

TOC Data Length

1

(LSB)

2

First Complete Session Number

3

Last Complete Session Number

 

0

Reserved

1

ADR

CONTROL

2

First Track Number In Last Complete Session

3

Reserved

4

.

.

7

(MSB)

Start Address of First Track in Last Session

 

(LSB)

Рисунок 3. Блок информации о сессиях

Здесь First и Last Complete Session Number – номера первой и последней завершенной сессии, First Track Number In Last Complete Session – номер первого трека в последней завершенной сессии, Start Address of First Track in Last Session – стартовый адрес этого трека. Формат блока информации о сессиях описывает следующая структура:

typedef struct {

      __u8 res1;

      __u8 ctrl  :4;

      __u8 adr   :4;

      __u8 first_trk; // номер первого трека посл. сессии

      __u8 res2;

      __u32 start_addr; // стартовый адрес трека

}  ms_info_t;

Чтение блока информации о сессиях выполняет функция read_ms_info():

void read_ms_info()

{

      int first_trk = 0; // номер первого трека посл. сессии

      __u8 read_toc_cmd[10]; // CDB - Command Descriptor Block

      __u8 data_buff[12];

      __u32 start_addr = 0; // стартовый адрес трека

      ms_info_t *ms_info;

      memset(data_buff, 0, 12);

      ms_info = (void *)(data_buff + 4);

/* Формируем CDB */

      memset(read_toc_cmd, 0, 10);   

      read_toc_cmd[0] = READ_TOC;

      read_toc_cmd[2] = 1; // Format = 1, Multi-session Information

      read_toc_cmd[8] = 12; // размер блока данных

      send_cmd(read_toc_cmd, 10, SG_DXFER_FROM_DEV, data_buff, 12, 20);

      first_trk = ms_info->first_trk;

      start_addr = __swab32(ms_info->start_addr);

      printf("Первый трек последней сессии - %d\n", first_trk);

      printf("Стартовый адрес трека - %u\n", start_addr);

      return;

}

Осталось узнать значение числа B – стартового адреса следующей возможной области программ. Этот адрес является адресом невидимого трека (invisible track), и определить его можно при помощи команды READ TRACK INFORMATION. Формат этой команды и пример использования был рассмотрен в [5]. Следующая функция выполняет чтение искомого адреса:

__u32 read_track_info(int trk_num)

{

      __u8 read_track_info_cmd[10];

      __u8 data_buff[40];

      __u32 lba = 0;

      memset(data_buff, 0, 40);

      memset(read_track_info_cmd, 0, 10);

      read_track_info_cmd[0] = 0x52;

      read_track_info_cmd[1] = 1;

      read_track_info_cmd[5] = trk_num; // 0xFF - invisible track

      read_track_info_cmd[8] = 40;

      send_cmd(read_track_info_cmd, 10, SG_DXFER_FROM_DEV, data_buff, 40, 20);

      memcpy((void *)&lba, (void *)(data_buff + 8), 4);

      return __swab32(lba);

}

В результате работы двух рассмотренных функций – read_ms_info() и read_track_info() – мы получим для имеющегося в нашем распоряжении трехсессионного диска значения чисел А и В: A = 23510 и B = 32534. Теперь можно сформировать ISO-образ для записи на диск четвертой сессии:

mkisofs -R -J -C 23510,32534 -M [имя файла устройства] -o track-04.iso [входной файл]

Перед тем как приступить к записи информации, целесообразно выяснить, в каком состоянии находится диск, т.е. можно ли дописать на него новую сессию. Эту информацию можно получить при помощи команды READ DISK INFORMATION. Формат этой команды представлен на рис. 4.

READ DISC INFORMATION CDB

Bit

Byte

7

6

5

4

3

2

1

0

0

Operation Code (51h)

1

Reserved

2

Reserved

3

Reserved

4

Reserved

5

Reserved

6

Reserved

7

(MSB)

Allocation Length

8

(LSB)

9

Control

Рисунок 4. Формат команды READ DISK INFORMATION

Формат данных, возвращаемых устройством по этой команде, представлен на рис. 5.

Необходимую нам информацию содержит поле Disk Status. Поле может принимать следующие значения:

  • 00b – Empty Disk, диск пустой, не содержит информации;
  • 01b – Incomplete Disk, незавершенный диск, на который можно дописывать информацию;
  • 10b – Finalized Disk, диск завершен, информацию дописывать нельзя.

Поле State of last Session описывает состояние последней сессии на диске. Поле может принимать следующие значения:

  • 00b пустая сессия (Empty Session);
  • 01b незавершенная сессия (Incomplete Session);
  • 11b сессия завершена (Complete Session).

Установленный в «1» бит Erasable свидетельствует о том, что в приводе находится диск CD-RW, DVD-RAM, DVD-RW или DVD+RW.

Назначение остальных полей приведено в спецификации [2], п. 6.27 «READ DISK INFORMATION Command».

Disc Information Block

Bit

Byte

7

6

5

4

3

2

1

0

0

(MSB)

Disc Information Length

1

(LSB)

2

Reserved

Erasable

State of last Session

Disc Status

3

Number of First Track on Disc

4

Number of Sessions (Least Significant Byte)

5

First Track Number in Last Session (Least Significant Byte)

6

Last Track Number in Last Session (Least Significant Byte)

7

DID_V

DBC_V

URU

DAC_V

Reserved

Dbit

BG Format Status

8

Disc Type

9

Number of Sessions (Most Significant Byte)

10

First Track Number in Last Session (Most Significant Byte)

11

Last Track Number in Last Session (Most Significant Byte)

12

(MSB)

Disc Identification

 

(LSB)

13

14

15

16

(MSB)

Last Session Lead-in Start Address

 

(LSB)

17

18

19

20

(MSB)

Last Possible Lead-out Start Address

 

(LSB)

21

22

23

24

(MSB)

...

Disc Bar Code

31

(LSB)

32

Disc Application Code

33

Number of OPC Tables

32-n

OPC Table Entries

Риунок 5. Блок информации о диске, возвращаемый по команде READ DISK INFORMATION

Чтение информации о диске выполняет функция read_ disk_info():

int read_disk_info()

{

      __u8 read_disk_info_cmd[10];

      __u8 data_buff[34];

      memset(data_buff, 0, 34);

      memset(read_disk_info_cmd, 0, 10);

      read_disk_info_cmd[0] = 0x51;

      read_disk_info_cmd[8] = 34;

      send_cmd(read_disk_info_cmd, 10, SG_DXFER_FROM_DEV, data_buff, sizeof(data_buff), 20);

      switch(data_buff[2] & 3) {

      case(0):

      printf("Диск пустой\n");

           break;

      case(1):

           printf("Можно дописывать информацию\n");

           break;

      case(2):

           printf("Запись невозможна\n");

           break;

    default:

           break;

      }

      return 0;

}

Если диск позволяет выполнить запись новой сессии (disk_status == 1), то алгоритм работы программы следующий:

  • при помощи системного вызова stat определяется размер записываемого образа в блоках по 2048 байт (режим Data Mode 1):

 #include <sys/stat.h>

    struct stat s;

    memset((void *)&s, 0, sizeof(struct stat));

/* argv[1] - имя файла-образа, передается в командной строке программы */

    if(stat(argv[1], &s)) {

        perror("stat");

        exit(errno);

    }

/* Размер файла-образа в блоках */

    track_size = s.st_size/CD_FRAMESIZE;

  • резервируется место для нового трека (команда RESERVE TRACK), при этом размер трека равен размеру файла-образа в блоках. Формат команды RESERVE TRACK был рассмотрен в [5]:

void reserv_track(__u32 track_size)

{

      __u8 reserv_track_cmd[10];

      __u32 size = 0;

      memset(reserv_track_cmd, 0, 10);

      reserv_track_cmd[0] = 0x53;

      size = __swab32(track_size);

      memcpy((void *)(reserv_track_cmd + 5), (void *)&size, 4);

      send_cmd(reserv_track_cmd, 10, SG_DXFER_NONE, NULL, 0, 20);

      return;

}

  • при помощи команды READ TRACK INFORMATION определяется стартовый адрес зарезервированного трека, и начиная с этого адреса на диск выполняется запись данных.

Полный текст программы для записи многосессионных дисков находится по адресу: http://bob.netport.com.ua/iso2cd_multi.tar.gz.

Литература:

  1. ATAPI DVD Devices, ftp://ftp.seagate.com/sff/INF-8090.PDF
  2. SCSI MultiMedia Command Set, http://www.t10.org/ftp/t10/drafts/mmc5/mmc5r01.pdf.
  3. Мешков В. Пакетные команды интерфейса ATAPI. – Журнал «Системный администратор», № 9(22), сентябрь 2004 г. – 70-84 с.
  4. Мешков В. Запись дисков CD-R/RW в Linux. Часть 1. – Журнал «Системный администратор», № 11(24), ноябрь 2004 г. – 56-62 с.
  5. Мешков В. Запись дисков CD-R/RW в Linux. Часть 2. – Журнал «Системный администратор», № 12(25), декабрь 2004 г. – 68-75 с.

Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-41
Fax: (499) 277-12-45
E-mail: sa@samag.ru