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

  Опросы
  Статьи

Электронный документооборот  

5 способов повысить безопасность электронной подписи

Область применения технологий электронной подписи с каждым годом расширяется. Все больше задач

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

Рынок труда  

Системные администраторы по-прежнему востребованы и незаменимы

Системные администраторы, практически, есть везде. Порой их не видно и не слышно,

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

Учебные центры  

Карьерные мечты нужно воплощать! А мы поможем

Школа Bell Integrator открывает свои двери для всех, кто хочет освоить перспективную

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

Гость номера  

Дмитрий Галов: «Нельзя сказать, что люди становятся доверчивее, скорее эволюционирует ландшафт киберугроз»

Использование мобильных устройств растет. А вместе с ними быстро растет количество мобильных

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

Прошу слова  

Твердая рука в бархатной перчатке: принципы soft skills

Лауреат Нобелевской премии, специалист по рынку труда, профессор Лондонской школы экономики Кристофер

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

1001 и 1 книга  
19.03.2018г.
Просмотров: 9932
Комментарии: 0
Потоковая обработка данных

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

19.03.2018г.
Просмотров: 8141
Комментарии: 0
Релевантный поиск с использованием Elasticsearch и Solr

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

19.03.2018г.
Просмотров: 8247
Комментарии: 0
Конкурентное программирование на SCALA

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

19.03.2018г.
Просмотров: 5222
Комментарии: 0
Машинное обучение с использованием библиотеки Н2О

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

12.03.2018г.
Просмотров: 5906
Комментарии: 0
Особенности киберпреступлений в России: инструменты нападения и защита информации

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

Друзья сайта  

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

Архив номеров / 2004 / Выпуск №12 (25) / Запись дисков CD-R/RW в Linux. Часть 2

Рубрика: Администрирование /  Оборудование   | Дополнительные материалы

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

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

Во второй части статьи рассмотрены примеры программ, выполняющих запись различной информации – музыкальных треков и данных – на компакт-диск CD-R/RW.

Запись информации на компакт-диск

Запись данных на компакт-диск

Рассмотрим пример программы, выполняющей запись данных на компакт-диск. Под термином «данные» понимается файл в формате ISO (файл-образ), полученный при помощи утилиты mkisofs или каким-либо другим способом.

Исходные тексты всех программ доступны на сайте журнала (www.samag.ru/source).

Алгоритм выполнения операции записи данных на компакт-диск следующий:

  • Определяются текущие параметры режима записи устройства путем считывания страницы параметров режима записи, код 0x05.
  • Устанавливаются требуемые значения полей страницы параметров режима записи – режим записи, тип блока данных, режим трека (аудио/данные) и др.
  • Из файла образа считываются блоки данных, и команда WRITE_10 выполняет запись этих блоков в соответствующие сектора компакт-диска. Стартовый номер сектора, с которого начинается запись, равен 0. Размер блока определяет значение поля «Data Block Type» страницы параметров режима записи (см. рис. 15 первой части статьи).
  • По окончании записи данных (достигнут конец файла-образа) последовательно выполняются команды SYNC-HRONIZE CACHE, CLOSE TRACK, CLOSE SESSION. По команде SYNCHRONIZE CACHE все данные, подлежащие записи, переносятся на носитель, кэш устройства освобождается. Команда CLOSE TRACK завершает трек, а CLOSE SESSION завершает сессию – формирует Lead-In и Lead-Out-области сессии (диска).

Для определения текущих параметров устройства применяется команда MODE SENSE, а для установки параметров – команда MODE SELECT. Формат команды MODE SENSE приведен на рисунке:

Mode Sense CDB

Bit

Byte

7

6

5

4

3

2

1

0

0

Operation code (5Ah)

1

Reserved

LLBAA

DBD

Reserved

2

PC

Page Code

3

Reserved

4

Reserved

5

Reserved

6

Reserved

7

(MSB)

Allocation Length

(LSB)

8

9

Control

Рисунок  1. Формат команды MODE SENSE

Поле Page Code содержит код запрашиваемой страницы режимов, поле Allocation Length – размер считываемых данных.

Формат команды MODE SELECT приведен на рисунке:

Mode Select CDB

Bit

Byte

7

6

5

4

3

2

1

0

0

Operation code (55h)

1

Reserved

PF=1

Reserved

SP

2

Reserved

3

Reserved

4

Reserved

5

Reserved

6

Reserved

7

(MSB)

Parameter List Length

(LSB)

8

9

Control

Рисунок 2. Формат команды MODE SELECT

Поле Parameter List Length содержит размер передаваемого списка параметров в байтах. Бит PF (Page Format) установлен в единицу. Это означает, что формат страницы соответствует стандарту SCSI.

Запись данных на диск выполняет команда WRITE_10. Формат этой команды представлен на рис. 3.

WRITE (10) CDB

Bit

Byte

7

6

5

4

3

2

1

0

0

Operation code (2Ah)

1

Reserved

DPO

FUA

Reserved

RelAdr

2

(MSB)

Logical Block Address

 

(LSB)

3

4

5

6

Reserved

7

(MSB)

Transfer Length

(LSB)

8

9

Control

Рисунок 3. Формат команды WRITE_10

Назначение полей:

  • Logical Block Address – адрес блока, в который будет записана информация.
  • Transfer Length – число блоков для записи на диск.

В команде SYNCHRONIZE CACHE используется только нулевой байт – он содержит код операции 0x35.

Формат команды CLOSE TRACK/SESSION представлен на рисунке:

CLOSE TRACK/SESSION CDB

Bit

Byte

7

6

5

4

3

2

1

0

0

Operation code (5Bh)

1

Reserved

IMMED

2

Reserved

Close Function

3

Reserved

4

(MSB)

Track Number

(LSB)

5

6

Reserved

7

Reserved

8

Reserved

9

Control Byte

Рисунок 4. Формат команды CLOSE TRACK/SESSION

Поле Close Function может принимать следующие значения:

  • 001b – закрыть трек, номер которого указан в поле Track Number.
  • 010b – закрыть последнюю незавершенную сессию, значение поля Track Number игнорируется.

Следующая структура описывает формат страницы параметров режима записи (см. рис. 15 в первой части статьи):

typedef struct {

    __u8 page_code      :6;

    __u8 rez            :1;

    __u8 ps             :1;

    __u8 page_length;

    __u8 write_type     :4;

    __u8 test_write     :1;

    __u8 ls_v           :1;

    __u8 BUFE           :1;

    __u8 rez1           :1;

    __u8 track_mode     :4;

    __u8 copy           :1;

    __u8 FP             :1;

    __u8 multises       :2;

    __u8 dbt            :4;

    __u8 rez2           :4;

    __u8 link_size;

    __u8 rez3;

    __u8 hac            :6;

    __u8 rez4           :2;

    __u8 s_format;

    __u8 rez5;

    __u32 packet_size;

    __u16 apl;

    __u8 mcn[16];

    __u8 isrc[16];

    __u32 sh;

} __attribute__ ((packed)) wpm_t;

Определим глобальные переменные:

// дескриптор файла sg-устройства

int sg_fd;

// указатель на данные страницы параметров режима записи

wpm_t *wpm;

// значение поля Mode Data Length заголовка Mode Parameter Header

__u16 mode_page_len = 0;

// размер списка страниц режима Mode Parameter List – 8 байт заголовка списка Mode Parameter Header + 52 байта самой

// страницы (см. рис. 13-15, первой части статьи)

__u16 data_len = 60;

// блок памяти для чтения списка Mode Parameter List

__u8 data_buff[60];

Функция mode_sense() считывает страницу параметров режима записи устройства:

int mode_sense()

{

    __u8 mode_sense_cmd[10];

/* Устройства готово? */

    if(test_unit_ready() < 0) exit(-1);

    memset(data_buff, 0, 60);

    // wpm указывает на начало страницы параметров режима записи

    wpm = (void *)(data_buff + 8);

/* Формируем командый пакет */

    memset(mode_sense_cmd, 0, 10);

    // код команды MODE SENSE

    mode_sense_cmd[0] = MODE_SENSE_10;

    // код страницы параметров режима записи

    mode_sense_cmd[2] = 5;

    // размер данных - 60 байт

    mode_sense_cmd[8] = data_len;

/* Посылаем команду устройству */

    if(send_cmd(mode_sense_cmd, 10, SG_DXFER_FROM_DEV, data_buff, data_len, 20000) < 0) return -1;

/* Размер считанных данных – значение поля Mode Data Length заголовка списка страниц */

    memcpy((void *)&mode_page_len, data_buff, 2);

    mode_page_len = __swab16(mode_page_len);

    printf("Mode data length - %d\n", mode_page_len);

/* Отобразим текущие параметры режима записи */

    // код страницы

    printf("Page code - %d\n", wpm->page_code);

    // размер страницы

    printf("Page length - %d\n", wpm->page_length);

    // режим записи

    printf("Write type - %d\n", wpm->write_type);

    // тип блока данных

    printf("Data block type - %d\n", wpm->dbt);

    // формат сессии

    printf("Session format - %d\n", wpm->s_format);

    // режим трека (поле Control Field)

    printf("Track mode  - %d\n", wpm->track_mode);

    return 0;

}

Установку требуемых параметров режима записи выполняет функция mode_select(). Этой функции мы передаем два параметра – тип блока данных (поле dbt структуры wpm_t) и тип информации, находящейся в треке (поле track_mode структуры wpm_t):

int mode_select(__u8 dbt, __u8 track_mode)

{

    __u8 mode_select_cmd[10];

/* Определяем текущие параметры режима записи */

    if(mode_sense() < 0) exit(-1);

/* Устанавливаем параметры для записи данных */

    wpm->write_type = 1; // режим записи – TAO

    wpm->dbt = dbt; // тип блока данных

    wpm->s_format = 0; // открытие новой сессии запрещено

    wpm->track_mode = track_mode; // режим трека

/* Устройство готово? */

    if(test_unit_ready() < 0) exit(-1);

/* Формируем командный пакет */

    memset(mode_select_cmd, 0, 10);

    // код команды MODE SELECT

    mode_select_cmd[0] = MODE_SELECT_10;

    // устанавливаем в 1 бит PF (Page Format)

    mode_select_cmd[1] = 1 << 4;

/* В поле Parameter List Header записываем размер блока данных */

    data_len = 60;

    mode_page_len = __swab16(data_len);

    memcpy((void *)(mode_select_cmd + 7),(void *)&mode_page_len, 2);

/* Посылаем устройству команду */

    if(send_cmd(mode_select_cmd, 10, SG_DXFER_TO_DEV, data_buff, data_len, 20000) < 0) return -1;

/* Для контроля отобразим установленные параметры режима записи */

    if(mode_sense() < 0) exit(-1);

    return 0;

}

Запись данных на компакт-диск выполняет функция write_iso(). Входные параметры функции – указатель на строку, содержащую имя файла-образа:

int write_iso(__u8 *file_name)

{

    int in_f;

    __u8 write_cmd[10];

    __u8 write_buff[CD_FRAMESIZE];

    __u32 lba = 0, lba1 = 0;

    if(test_unit_ready() < 0) exit(-1);

/* Открываем файл-образ */

    in_f = open(file_name, O_RDONLY, 0600);

    memset(write_buff, 0, CD_FRAMESIZE);

/* Цикл чтения блоков данных из файла. Размер блока – 2048 байт */

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

/* Формируем командный пакет */

    memset(write_cmd, 0, 10);

    write_cmd[0] = WRITE_10; // код команды

    write_cmd[8] = 1; // количество секторов для записи

    printf("%c", 0x0D);

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

/* Адрес блока для записи */

    lba = __swab32(lba1);

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

    lba1 += 1;

/* Посылаем устройству команду */

    if(send_cmd(write_cmd, 10, SG_DXFER_TO_DEV, write_buff, CD_FRAMESIZE, 20000) < 0) return -1;

    }

    printf("\n");

    return 0;

}

По окончании записи данных необходимо, как было уже сказано выше, послать устройству три команды: SYNCHRO-NIZE CACHE, CLOSE TRACK, CLOSE SESSION. Команду SYNCHRONIZE CACHE формирует и посылает устройству функция sync_cache():

int sync_cache()

{

    __u8 flush_cache[10];

    if(test_unit_ready() < 0) exit(-1);

/* Формируем и посылаем команду */

    memset(flush_cache, 0, 10);

    flush_cache[0] = SYNCHRONIZE_CACHE; // код команды

    if(send_cmd(flush_cache, 10, SG_DXFER_NONE, NULL, 0, 20000) < 0) return -1;

    return 0;

}

Закрывает трек функция close_track():

int close_track()

{

    __u8 close_trk_cmd[10];

    if(sync_cache() < 0) {

    printf("Cannot synchronize cache\n");

    return -1;

    }

    if(test_unit_ready() < 0) exit(-1);

/* Формируем команду закрытия трека и посылаем ее устройству */

    memset(close_trk_cmd, 0, 10);

    close_trk_cmd[0] = 0x5B; // код команды CLOSE TRACK

    close_trk_cmd[2] = 1; // флаг закрытия трека

    close_trk_cmd[5] = 1; // номер трека, он у нас один

    if(send_cmd(close_trk_cmd, 10, SG_DXFER_NONE, NULL, 0, 20000) < 0) return -1;

    return 0;

}

Функция close_session() закрывает текущую сессию:

int close_session()

{

    __u8 close_sess_cmd[10];

    if(test_unit_ready() < 0) exit(-1);

    memset(close_sess_cmd, 0, 10);

    close_sess_cmd[0] = 0x5B; // код команды CLOSE SESSION

    close_sess_cmd[2] = 2; // флаг закрытия текущей сессии

    if(send_cmd(close_sess_cmd, 10, SG_DXFER_NONE, NULL, 0, 60000) < 0) return -1;

    return 0;

}

После того как данные записаны, извлекаем компакт-диск из привода, послав устройству команду START/STOP UNIT (см. [5]):

void eject_cd()

{

    __u8 start_stop_cmd[6];

    memset(start_stop_cmd, 0, 6);

    start_stop_cmd[0] = 0x1B; // код команды START/STOP UNIT

    start_stop_cmd[4] = 2; // извлечь компакт-диск

    send_cmd(start_stop_cmd, 6, SG_DXFER_NONE, NULL, 0, 20000);

    return;

}

Имя файла-образа передается главной функции программы в виде параметра. Функция main() выглядит следующим образом:

int main(int argc, char **argv)

{

/* Проверяем наличие входных параметров */

    if(argc != 2) {

    printf("\n\tUsage: write_iso [ISO-image]\n\n");

    return 0;

    }

/* Открываем файл sg-устройства */

    if((sg_fd = open(SG_DEV, O_RDWR)) < 0) {

           perror("open");

           return -1;

    }

/* Устанавливаем параметры режима записи данных: тип блока данных dbt = 8 – формат блока Mode 1,

 * размер блока 2048 байт; тип информации, находящейся в треке track_mode = 4 – трек содержит данные.

 */

    mode_select(8, 4);

/* Выполняем запись данных на компакт-диск */

    if(write_iso(argv[1]) < 0) {

    printf("Cannot write image %s\n", argv[1]);

    return -1;

    }

/* Закрываем трек, сессию и извлекаем компакт-диск из привода*/

    close_track();

    close_session();

    eject_cd();

    close(sg_fd);

    return 0;

}

Полный текст программы записи ISO-образа на компакт-диск находится в файле SG/iso_write.c.

Запись аудиоданных

Алгоритм записи аудиоданных практически не отличается от приведенного ранее алгоритма записи данных. Но так как в одной сессии теперь будет находиться несколько треков, то при записи аудиотрека на компакт-диск необходимо будет знать стартовый адрес трека. Этот адрес можно вычислить самостоятельно, можно определить его как адрес невидимого, незавершенного трека (invisible, incomplete track) либо предварительно зарезервировать на компакт-диске пространство для каждого трека.

Резервирование пространства для трека выполняет команда RESERVE TRACK. Формат этой команды представлен на рисунке:

RESERVE TRACK CDB

Bit

Byte

7

6

5

4

3

2

1

0

0

Operation code (53h)

1

Reserved

2

Reserved

3

Reserved

4

Reserved

5

(MSB)

Reservation

Size

(LSB)

6

7

8

9

Control Byte

Рисунок 5. Формат команды RESERVE TRACK

В поле Reservation size указывается размер трека в блоках. Каждый вызов команды RESERVE TRACK модифицирует PMA-область, добавляя в нее запись о координатах нового трека (таблица 4 первой части статьи). Считать информацию о координатах трека можно при помощи команды READ TRACK INFORMATION. Формат команды:

READ TRACK INFORMATION CDB

Bit

Byte

7

6

5

4

3

2

1

0

0

Operation code (52h)

1

Reserved

Address/Number Type

2

(MSB)

Logical Block Address/

Track/Session Number

(LSB)

3

4

5

6

Reserved

7

(MSB)

Allocation Length

(LSB)

8

9

Control Byte

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

Значение поля Address/Number Type определяет содержание поля Logical Block Address/Track/Session Number. Если Address/Number Type = 01b, то устройство вернет блок информации о треке, номер которого находится в поле Logical Block Address/Track/Session Number. Формат блока информации о треке представлен на рисунке.

Track Information Block

Bit

Byte

7

6

5

4

3

2

1

0

0

(MSB)

Data Length

(LSB)

1

2

Track Number (Least Significant Byte)

3

Session Number  (Least Significant Byte)

4

Reserved

5

Reserved

Damage

Copy

Track Mode

6

RT

Blank

Packet/Inc

FP

Data Mode

7

Reserved

LRA_V

NWA_V

8

(MSB)

Track Start

Address

(LSB)

9

10

11

12

(MSB)

Next Writable

Address

(LSB)

13

14

15

16

(MSB)

Free

Blocks

(LSB)

17

18

19

20

(MSB)

Fixed Packet Size/

Blocking Factor

(LSB)

21

22

23

24

(MSB)

Track Size

 

(LSB)

25

26

27

28

(MSB)

Last Recorded Address

 

(LSB)

29

30

31

32

Track Number   (Most Significant Byte)

33

Session Number   (Most Significant Byte)

34

Reserved

35

Reserved

36

(MSB)

Read Compatibility LBA

(LSB)

……

39

Рисунок 7. Формат блока информации о треке

Из всего многообразия данных, находящихся в блоке, нас интересует только поле Track Start Address. Это поле содержит стартовый адрес трека, с этого адреса выполняется запись трека. Назначение остальных полей приведено в спецификации SCSI MMC-4 ([1]).

Функция reserv_track() выполняет резервирование на компакт-диске пространства для трека. Входной параметр функции – размер трека в блоках.

int reserv_track(__u32 track_size)

{

    __u8 reserv_track_cmd[10];

    __u32 size = 0;

    if(test_unit_ready() < 0) exit(-1);

/* Формируем командный пакет */

    memset(reserv_track_cmd, 0, 10);

    reserv_track_cmd[0] = 0x53; // код команды RESERVE TRACK

/* Заполняем поле Reservation size */

    size = __swab32(track_size);

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

    if(send_cmd(reserv_track_cmd, 10, SG_DXFER_NONE, NULL, 0, 20000) < 0) return -1;

/* Cчитываем PMA */

    read_pma();

    return 0;

}

Чтение информации о треке выполняет функция read_track_info(). Параметр функции – номер трека. Возвращаемое значение – стартовый адрес трека.

__u32 read_track_info(int trk_num)

{

    __u8 read_track_info_cmd[10];

    __u8 data_buff[40]; // блок информации о треке

    __u16 len = 40; // размер блока

    __u32 lba = 0;

    if(test_unit_ready() < 0) exit(-1);

/* Формируем командный пакет */

    memset(data_buff, 0, 40);

    memset(read_track_info_cmd, 0, 10);

    read_track_info_cmd[0] = 0x52;

    // поле Address/Number Type = 01b

    read_track_info_cmd[1] = 1;

    read_track_info_cmd[5] = trk_num; // номер трека

    len = __swab16(len);

    memcpy((void *)(read_track_info_cmd + 7), (void *)&len, 2);

    if(send_cmd(read_track_info_cmd, 10, SG_DXFER_FROM_DEV, data_buff, 40, 20000) < 0) {

    printf("Cannot read track #%d info!\n", trk_num);

    exit(-1);

    }

/* Из блока информации о треке считываем данные о размере трека (поле Track Size ) */

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

    printf("Track #%d size - %u sectors\n", trk_num, __swab32(lba));

/* Стартовый адрес трека */

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

    return __swab32(lba);

}

Рассмотрим главную функцию программы записи аудиоданных. Входные параметры – список файлов в формате Ogg Vorbis.

int main(int argc, char **argv)

{

    int i = 1;

    struct stat s;

    __u32 start_lba = 0, total_sectors = 0;

    __u32 track_size = 0;

/* Проверяем наличие входных параметров */

    if(argc == 1) {

    printf("\n\tUsage: write_audio [OGG-files]\n\n");

    return 0;

    }

/* Открываем файл устройства */

    sg_fd = open(SG_DEV, O_RDWR);

/* Устанавливаем параметры режима записи: тип блока данных dbt = 0 – «сырые» данные, размер блока 2352 байта;

 * track_mode = 0 – трек содержит аудиоданные.

 */

    mode_select(0, 0);

/* Считываем информацию из PMA */

    printf("Display PMA:\n");

    read_pma();

/* Цикл записи треков на диск */

    for(i = 1; i < argc; i++) {

/* Преобразуем файл из формата Ogg Vorbis в WAV (без заголовка). Имя выходного файла – track.cdr */

    ogg_decoder(argv[i]);

/* Определяем размер файла track.cdr в байтах*/

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

    stat("./track.cdr", &s);

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

    track_size = s.st_size / CD_FRAMESIZE_RAW;

    printf("File size - %u\n", s.st_size);

    printf("Sectors in file - %u\n", track_size);

    printf("Reserve track #%d\n", i);

/* Резервируем пространство для трека */

    reserv_track(track_size);

/* Определяем стартовый адрес трека */

    start_lba = read_track_info(i);

    printf("Start LBA for track #%d - %u\n", i, start_lba);

/* Записываем в трек аудиоданные. Номер стартового блока равен start_lba */

    total_sectors = write_audio("./track.cdr", start_lba);

    if(total_sectors == 0) return -1;

/* Закрываем трек */

    close_track(i);

    }

/* Цикл записи треков завершен. Закрываем сессию */

    close_session();

/* Удаляем файл track.cdr и извлекаем диск из привода */

    unlink("./track.cdr");

    eject_cd();

    close(sg_fd);

    return 0;

}

Преобразование файла из формата Ogg Vorbis в WAV выполняет функция ogg_decoder(). Входной параметр функции – имя файла в формате Ogg Vorbis. Для конвертирования используются библиотека libvorbis (http://www.vorbis.org). На выходе получается файл track.cdr в формате WAV, но без RIFF-заголовка.

void ogg_decoder(__u8 *file_name)

{

    __u8 pcmout[8192];

    FILE *f;

    int out;

    OggVorbis_File vf;

    int current_section;

    printf("\nDecoding file %s..", file_name);

    f = fopen(file_name,"r");

    out = open("./track.cdr", O_CREAT|O_RDWR|O_TRUNC, 0600);

    if(ov_open(f, &vf, NULL, 0) < 0) {

    printf("Input does not appear to be an Ogg bitstream.\n");

    exit(-1);

    }

    for(;;) {

           memset(pcmout, 0, 8192);

           long ret = ov_read(&vf, pcmout, sizeof(pcmout), 0, 2, 1, &current_section);

           if (ret == 0) break;

           if(ret < 0) {

                 printf("Error OGG bitsream");

                 exit(-1);

           }

           if(write(out, pcmout, ret) < 0) {

                 printf("write");

                 exit(-1);

           }

    }

    fclose(f);

    close(out);

    printf("OK\n");

    return;

}

Функция write_audio() выполняет запись аудиотрека на диск. Входные параметры – имя файла WAV-формата (без заголовка), содержащего аудиоданные, и стартовый адрес, с которого начинается запись. Возвращаемое значение – общее число записанных на диск блоков:

__u32 write_audio(__u8 *file_name, __u32 start_lba)

{

    int in_f;

    __u8 write_cmd[10];

    // блок для  аудиоданных, 2352 байт

    __u8 write_buff[CD_FRAMESIZE_RAW];

    __u32 lba = 0, lba1 = 0;

    if(test_unit_ready() < 0) exit(-1);

    lba1 = start_lba;

/* Открываем файл с аудиоданными */

    in_f = open(file_name, O_RDONLY, 0600);

    memset(write_buff, 0, CD_FRAMESIZE_RAW);

/* Цикл чтения данных из файла и записи */

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

/* Формируем командный пакет */

    memset(write_cmd, 0, 10);

    write_cmd[0] = WRITE_10;

    write_cmd[8] = 1; // число блоков для записи

    printf("%c", 0x0D);

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

    // адрес блока, в который выполняется запись данных

    lba = __swab32(lba1);

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

    lba1 += 1;

    if(send_cmd(write_cmd, 10, SG_DXFER_TO_DEV, write_buff, CD_FRAMESIZE_RAW, 20000) < 0) return 0;

    memset(write_buff, 0, CD_FRAMESIZE_RAW);

    }

    close(in_f);

    printf("\n");

    return lba1; // число блоков, записанных на диск

}

Полный текст программы создания аудиодиска из файлов формата Ogg Vorbis находится с файле SG/ogg2cdda.c.

Получаем исполняемый файл при помощи команды:

# gcc -o ogg2cdda ogg2cdda.c -lvorbis -lvorbisfile

Рассмотрим пример работы программы ogg2cdda. Устанавливаем в привод диск CD-RW и запускаем программу на выполнение, указав в командной строке имена трех файлов в формате Ogg Vorbis:

# ./ogg2cdda trk1.ogg trk2.ogg trk3.ogg

Результаты работы программы:

Decoding file trk1.ogg..OK

File size – 41505744 bytes

Sectors in file - 17647

Reserve track #1

PMA data length - 24

PMA entries - 2

Entry      ADR      CTRL      Point      Min      Sec      Frame      PMin      Psec      Pframe            LBA

0            2      0      0      80      71      82      0      0      0            ---

1            1      0      1      3      57      24      0      2      0            0

Track #1 size - 17647 sectors

Start LBA for track #1 - 0

 

Decoding file trk2.ogg..OK

File size – 36604176 bytes

Sectors in file - 15563

Reserve track #2

PMA data length - 35

PMA entries - 3

Entry      ADR      CTRL      Point      Min      Sec      Frame      PMin      Psec      Pframe            LBA

0            2      0      0      80      71      82      0      0      0            ---

1            1      0      1      3      57      24      0      2      0            0

2            1      0      2      7      26      64      3      59      24            17799

Track #2 size - 15563 sectors

Start LBA for track #2 - 17799

 

Decoding file trk3.ogg..OK

File size – 47684448 bytes

Sectors in file - 20274

Reserve track #3

PMA data length - 46

PMA entries - 4

Entry      ADR      CTRL      Point      Min      Sec      Frame      PMin      Psec      PFrame            LBA

0            2      0      0      80      71      82      0      0      0            ---

1            1      0      1      3      57      24      0      2      0            0

2            1      0      2      7      26      64      3      59      24            17799

3            1      0      3      11      59      15      7      28      64            33514

Track #3 size - 20274 sectors

Start LBA for track #3 - 33514

 

Close session..OK

Хорошо видно, что после каждого вызова функции reserv_track в PMA добавляется запись о новом треке. Расстояние между треками – 2 секунды.

Теперь рассмотрим пример записи аудиоданных без предварительного резервирования треков. Для определения стартового адреса трека при помощи команды READ TRACK INFORMATION считывается адрес невидимого, незавершенного (invisible) трека. В этом случае поле Address/Number Type команды READ TRACK INFORMATION содержит 01b, поле Logical Block Address/Track/Session Number – значение 0xFF (см. табл. 452, стр. 374 спецификации [1]).

Функция определения стартового адреса невидимого трека:

__u32 read_track_info()

{

    __u8 read_track_info_cmd[10];

    __u8 data_buff[40]; // блок информации о треке

    __u16 len = 40; // размер блока информации о треке

    __u32 lba = 0;

    if(test_unit_ready() < 0) exit(-1);

/* Формируем командный пакет */

    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] = 0xFF; // invisible track

    len = __swab16(len);

    memcpy((void *)(read_track_info_cmd + 7), (void *)&len, 2);

    if(send_cmd(read_track_info_cmd, 10, SG_DXFER_FROM_DEV, data_buff, 40, 20000) < 0) {

    printf("Cannot read track info!\n");

    exit(-1);

    }

/* Стартовый адрес невидимого трека. С этого адреса будет выполняться запись */

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

    return __swab32(lba);

}

Рассмотрим главную функцию программы записи аудиоданных. Входные параметры – список файлов в формате MP3.

int main(int argc, char **argv)

{

    int i = 1;

    __u32 start_lba = 0, total_sectors = 0;

/* Проверяем входные параметры */

    if(argc == 1) {

    printf("\n\tUsage: write_audio [MP3-files]\n\n");

    return 0;

    }

/* Открываем файл устройства */

    if((sg_fd = open(SG_DEV, O_RDWR)) < 0) {

           perror("open");

           return -1;

    }

/* Устанавливаем параметры режима записи (см. предыдущий пример) */

    mode_select(0, 0);

/* Цикл записи треков на диск */

    for(i = 1; i < argc; i++) {

/* Декодируем файл из MP3-формата в WAV */

    printf("\nDecoding file %s..\n", argv[i]);

    mp3_decoder(argv[i]);

/* Определяем стартовый адрес трека */

    start_lba = read_track_info();

    printf("Start LBA for track #%d - %u\n", i, start_lba);

/* Записываем трек на диск и закрываем трек */

    total_sectors = write_audio("./track.wav", start_lba);

    if(total_sectors == 0) return -1;

    close_track(i);

    }

/* Цикл записи треков завершился, закрываем сессию */

    close_session();

/* Удаляем файл track.wav и извлекаем диск из привода */

    unlink("./track.wav");

    eject_cd();

    close(sg_fd);

    return 0;

}

Преобразование файлов из формата MP3 в WAV выполняет функция mp3_decoder(). Входные параметры функции – имя файла формата MP3. На выходе получаем файл track.wav в формате WAV. Для конвертирования используется программа mpg321:

void mp3_decoder(__u8 *file_name)

{

    static pid_t pid;

    int status;

    switch(pid = fork()) {

    case -1:

           perror("fork");

           exit(-1);

    case 0:

           execl("/usr/bin/mpg321", "mpg321", "-q", "-w", "track.wav", file_name, 0);

           exit(-1);

    }

    if((pid = waitpid(pid, &status, 0)) && ї

           WIFEXITED(status)) return;

}

При записи WAV-файла необходимо отбросить его заголовок. В функции записи файла write_audio() после открытия файла необходимо сместиться к началу аудиоданных при помощи вызова:

lseek(in_f, WAV_HEADER_SIZE, 0)

где WAV_HEADER_SIZE = 44 – размер заголовка WAV-файла.

Полный текст программы создания аудиодиска из файлов формата MP3 находится в файле SG/mp2cd_inv_track.c.

Следующий фрагмент программы выполняет запись треков, и стартовый адрес мы вычисляем самостоятельно:

total_sectors = 0;

/* Цикл записи треков */

    for(i = 1; i < argc; i++) {

           printf("\nWriting track #%d:\n", i);

/* Определяем стартовый адрес трека и записываем трек на диск. После этого закрываем трек */

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

           if(i > 1) start_lba = total_sectors + apl + 2;

           printf("Start sector - %u\n", start_lba);

           total_sectors = write_audio(argv[i], start_lba);

           if(total_sectors == 0) return -1;

           close_track(i);

    }

Стартовый адрес первого трека нам известен – он равен нулю. Адрес нового трека вычисляется по формуле:

start_lba = total_sectors + apl + 2

где total_sector – общее число секторов, записанных на диск, apl – длина аудиопаузы, значение поля Audio Pause Length (APL) страницы параметров режима записи (рис. 15 первой части статьи).

Значение APL хранится в поле apl структуры wpm, и определяется следующим образом:

mode_sense();

apl = __swab16(wpm->apl);

В файле SG/write_audio.c находится полный текст программы, выполняющей запись WAV-файлов на аудиокомпакт. Стартовый адрес трека вычисляется вышеприведенным способом.

А теперь давайте сотрем с диска все, что мы на него записали. Для стирания информации с диска используется команда BLANK. Формат этой команды приведен на рис. 8.

Поле Blanking type устанавливает режим очистки диска. Может принимать следующие значения:

  • 000b – стирается вся информация, находящаяся на диске. Значение поля Start Address игнорируется.
  • 001b – минимальная очистка диска. Стирается PMA, Lead-In-область диска и pre-gap-область первого трека. Параметр Start Address игнорируется.

BLANK CDB

Bit

Byte

7

6

5

4

3

2

1

0

0

Operation code (A1h)

1

Reserved

IMMED

Reserved

Blanking Type

2

(MSB)

Start Address/Track Number

 

(LSB)

3

4

5

6

Reserved

7

Reserved

8

Reserved

9

Reserved

10

Reserved

11

Control Byte

Рисунок 8. Формат команды BLANK

Остальные значения поля Blanking type и их описание приведены в спецификации [1], табл. 219 «Blanking Types CD-RW and DDCD-RW».

Функция blank() выполняет очистку CD-RW-диска. Параметр функции blank_type устанавливает режим очистки диска. Допустимые значения этого параметра – 0 или 1:

int blank(__u8 blank_type)

{

    __u8 blank_cmd[12];

    if(test_unit_ready() < 0) exit(-1);

    memset(blank_cmd, 0, 12);

    blank_cmd[0] = 0xA1; // код команды BLANK

    // режим очистки: 0 – полная, 1 – минимальная

    blank_cmd[1] = blank_type;

    if(send_cmd(blank_cmd, 12, SG_DXFER_NONE, NULL, 0, 9600*1000) < 0) return -1;

    return 0;

}

Полный текст программы очистки CD-RW-диска приведен в файле SG/blank.c.

Общее замечание по работе всех программ. Иногда программа после запуска выдает сообщение об ошибке:

Sense data: 0x70 0x00 0x06 0x00 0x00 0x00 0x00 0x0a 0x00 0x00 0x00 0x00 0x28 0x00 0x00 0x00 0x00 0x00

Driver_status=0x28

Unit not ready

и завершает работу. Здесь Sense Key = 0x06, ASC = 0x28, ASCQ = 0x00 (см. [3, 5]), что означает «UNIT ATTENTION. Indicates that the removable medium may have been changed or the ATAPI CD-ROM Drive has been reset. NOT READY TO READY TRANSITION, MEDIUM MAY HAVE CHANGED» (Носитель был заменен или выполнен сброс ATAPI-контроллера).

При повторном запуске программа выполняется без ошибок.

Литература и ссылки:

  1. Спецификация SCSI Multimedia Commands-4 (SCSI MMC-4), http://www.t10.org/ftp/t10/drafts/mmc4/mmc4r03d.pdf.
  2. Спецификация SCSI-3 Multimedia Commands, http://www.t10.org/ftp/t10/drafts/mmc/mmc-r10a.pdf.
  3. Specification for ATAPI DVD Devices, ftp.seagate.com/sff/INF-8090.pdf.
  4. SCSI-Generic-HOWTO, http://www.linux.org/docs/ldp/howto/SCSI-Generic-HOWTO/index.html.
  5. Мешков В. «Пакетные команды интерфейса ATAPI» – Журнал «Системный администратор», № 9, сентябрь 2004 г. – 70-84 с.

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

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

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

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

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