hd.c Plik do obsługi przerwań do dysku niskiego poziomu. Implementacja funkcji kontroli stanu dysku i kontrolera Realizacja komunikacji na przerwaniach pomiędzy dyskiem i kontrolerem Autorzy: Mateusz Foks i Jakub Husak
Atutor tego fragmentu: Mateusz Foks

/* (linux/drivers/block/hd.c  *  *  Copyright (C) 1991, 1992  Linus Torvalds  */ /*  * This is the low-level hd interrupt support. It traverses the  * request-list, using interrupts to jump between functions. As  * all the functions are called within interrupts, we may not  * sleep. Special care is recommended.  *  *  modified by Drew Eckhardt to check nr of hd's from the CMOS.  *  *  Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug  *  in the early extended-partition checks and added DM partitions  *  *  IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",  *  and general streamlining by Mark Lord.  */ #define DEFAULT_MULT_COUNT  0   /* set to 0 to disable multiple mode at boot */ #define DEFAULT_UNMASK_INTR 0   /* set to 0 to *NOT* unmask irq's more often */ #include <asm/irq.h> #include <linux/errno.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/timer.h> #include <linux/fs.h> #include <linux/kernel.h> #include <linux/hdreg.h> #include <linux/genhd.h> #include <linux/malloc.h> #include <linux/string.h> #include <linux/ioport.h> #include <linux/mc146818rtc.h> /* CMOS defines */ #define REALLY_SLOW_IO #include <asm/system.h> #include <asm/io.h> #include <asm/segment.h> #define MAJOR_NR HD_MAJOR #include <linux/blk.h> static int revalidate_hddisk(kdev_t, int); #define HD_DELAY        0 // maksymalna liczba błędów podczas czytania i pisania #define MAX_ERRORS     16       /* Max read/write errors/sector */ // częstotliwość resetowania kontrolera #define RESET_FREQ      8       /* Reset controller every 8th retry */ // częstotliwoćś kalibrowania kontrolera #define RECAL_FREQ      4       /* Recalibrate every 4th retry */ #define MAX_HD          2 #define STAT_OK         (READY_STAT|SEEK_STAT) #define OK_STATUS(s)    (((s)&(STAT_OK|(BUSY_STAT|WRERR_STAT|ERR_STAT)))==STAT_OK) static void recal_intr(void); static void bad_rw_intr(void); static char recalibrate[MAX_HD] = { 0, }; static char special_op[MAX_HD] = { 0, }; static int access_count[MAX_HD] = {0, }; static char busy[MAX_HD] = {0, }; static struct wait_queue * busy_wait = NULL; static int reset = 0; static int hd_error = 0; /*  *  This struct defines the HD's and their types.  */ // struktura w której zapisane są informacje na temat typu i rodzaju dysku twardego struct hd_i_struct {         unsigned int head,sect,cyl,wpcom,lzone,ctl;  // liczba głowic, sektorów, cylindrów, ? , miejsce londowania głowicy, ?         }; static struct hd_driveid *hd_ident_info[MAX_HD] = {0, };   #ifdef HD_TYPE static struct hd_i_struct hd_info[] = { HD_TYPE }; struct hd_i_struct bios_info[] = { HD_TYPE }; static int NR_HD = ((sizeof (hd_info))/(sizeof (struct hd_i_struct))); #else static struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} }; struct hd_i_struct bios_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} }; static int NR_HD = 0; #endif static struct hd_struct hd[MAX_HD<<6]={{0,0},}; static int hd_sizes[MAX_HD<<6] = {0, }; static int hd_blocksizes[MAX_HD<<6] = {0, }; #if (HD_DELAY > 0) unsigned long last_req; // czyta czas systemowy unsigned long read_timer(void) {         unsigned long t, flags;         int i;         save_flags(flags);         cli();         t = jiffies * 11932;         outb_p(0, 0x43);         i = inb_p(0x40);         i |= inb(0x40) << 8;         restore_flags(flags);         return(t - i); } #endif // zbiera informacje na temat typu i rodzaju dysku void hd_setup(char *str, int *ints) {         int hdind = 0; // identyfikator dysku         if (ints[0] != 3)                 return;         if (bios_info[0].head != 0)                 hdind=1;         bios_info[hdind].head  = hd_info[hdind].head = ints[2]; // głowice         bios_info[hdind].sect  = hd_info[hdind].sect = ints[3]; // sektory         bios_info[hdind].cyl   = hd_info[hdind].cyl = ints[1]; // cylindry         bios_info[hdind].wpcom = hd_info[hdind].wpcom = 0;         bios_info[hdind].lzone = hd_info[hdind].lzone = ints[1]; // miejsce londowania głowicy         bios_info[hdind].ctl   = hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0); // parametr kontrolny         NR_HD = hdind+1; } // wypisuje na ekran informacje na temat stanu dysku static void dump_status (const char *msg, unsigned int stat) {         unsigned long flags;         char devc;         // ustala litere dysku         devc = CURRENT ? 'a' + DEVICE_NR(CURRENT->rq_dev) : '?';         save_flags (flags);         sti();         printk("hd%c: %s: status=0x%02x { ", devc, msg, stat & 0xff);         if (stat & BUSY_STAT)   printk("Busy "); // dysk zajenty         if (stat & READY_STAT)  printk("DriveReady "); // dysk gotowy         if (stat & WRERR_STAT)  printk("WriteFault "); // błąd zapisu         if (stat & SEEK_STAT)   printk("SeekComplete "); // wyszukiwanie zakończone         if (stat & DRQ_STAT)    printk("DataRequest ");  // żądanie danych         if (stat & ECC_STAT)    printk("CorrectedError ");  // błąd naprawiony         if (stat & INDEX_STAT)  printk("Index ");  // index dysku         if (stat & ERR_STAT)    printk("Error ");  // kod błędu         printk("}\n");         if ((stat & ERR_STAT) == 0) {   // ustala hd_error. Gdy nie ma blędu to 0                 hd_error = 0;         } else {  // gdy wystąpil bląd dysku                 hd_error = inb(HD_ERROR);                 printk("hd%c: %s: error=0x%02x { ", devc, msg, hd_error & 0xff);  // wypisuje ten błąd                 if (hd_error & BBD_ERR)         printk("BadSector ");  // zły sektor                 if (hd_error & ECC_ERR)         printk("UncorrectableError ");  // błąd nie do naprawienia                 if (hd_error & ID_ERR)          printk("SectorIdNotFound "); // sektor nie znaleziony                 if (hd_error & ABRT_ERR)        printk("DriveStatusError ");  // wystąpił błąd stanu dysku                 if (hd_error & TRK0_ERR)        printk("TrackZeroNotFound ");  // nie znalazł ścieżki zerowej na dysku                 if (hd_error & MARK_ERR)        printk("AddrMarkNotFound ");  // znacznik adresu nie znaleziony                 printk("}");                 if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {                         printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL),                                 inb(HD_CURRENT) & 0xf, inb(HD_SECTOR));                         if (CURRENT)                                 printk(", sector=%ld", CURRENT->sector);                 }                 printk("\n");         }         restore_flags (flags); } // sprawdza stan dysku void check_status(void) {         int i = inb_p(HD_STATUS);  // pobiera stan dysku         if (!OK_STATUS(i)) {  // gdy stan nie jest w porządku                 dump_status("check_status", i);  // wypisuje stan                 bad_rw_intr();         } } // sprawdza czy kontroler jest zajęty static int controller_busy(void) {         int retries = 100000;  // liczba powtórzeń         unsigned char status;         do {  // aktywne czekanie na kontroler                 status = inb_p(HD_STATUS);  // pobiera stan kontrolera do konkretnego dysku         } while ((status & BUSY_STAT) && --retries);  //         return status; } // sprawdza stan dysku i zwraca 1 gdy ok, 0 gdy nie static int status_ok(void) {         unsigned char status = inb_p(HD_STATUS);  // pobiera stan dysku         if (status & BUSY_STAT)                 return 1;       /* Ancient, but does it make sense??? */         if (status & WRERR_STAT)  // gdy błąd zapisu na dysk                 return 0;         if (!(status & READY_STAT))  // gdy gotowy                 return 0;         if (!(status & SEEK_STAT))  /gdy błąd wyszukiwania                 return 0;         return 1;  // w pozostalych przypadkach } // static int controller_ready(unsigned int drive, unsigned int head) {         int retry = 100;  // ilosc powtórzeń odwołań do kontrolera.         do {                 if (controller_busy() & BUSY_STAT) // gdy kontroler zajęty                         return 0;                 outb_p(0xA0 | (drive<<4) | head, HD_CURRENT);  // pobiera dane na temat bieżącego dysku. Robi to tylko wtedy, gdy kontroler nie jest zajęty.                 if (status_ok())  // gdy stan dysku jest ok                         return 1;         } while (--retry);  // czeka aktywnie na odebranie danych         return 0; } // odwołuje się  do określonego dysku i okreslonego miejsca na nim i liczby sektorów // cmd - komenda jaką ma realizować dysk // intr_addr - adres funkcji która będzie wywołana gdy dysk wyśle przerwanie static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,                 unsigned int head,unsigned int cyl,unsigned int cmd,                 void (*intr_addr)(void)) {         unsigned short port; static void hd_request (void); static unsigned int identified  [MAX_HD] = {0,}; /* 1 = drive ID already displayed   */ static unsigned int unmask_intr [MAX_HD] = {0,}; /* 1 = unmask IRQs during I/O       */ static unsigned int max_mult    [MAX_HD] = {0,}; /* max sectors for MultMode         */ static unsigned int mult_req    [MAX_HD] = {0,}; /* requested MultMode count         */ static unsigned int mult_count  [MAX_HD] = {0,}; /* currently enabled MultMode count */ static struct request WCURRENT; // naprawia niepoprawne napisy static void fixstring (unsigned char *s, int bytecount) {         unsigned char *p, *end = &s[bytecount &= ~1];   /* bytecount must be even */         /* convert from big-endian to little-endian */         for (p = end ; p != s;) {                 unsigned short *pp = (unsigned short *) (p -= 2);                 *pp = (*pp >> 8) | (*pp << 8);         }         // nie czyta pustych początków         /* strip leading blanks */         while (s != end && *s == ' ')                 ++s;         /* compress internal blanks and strip trailing blanks */         while (s != end && *s) {                 if (*s++ != ' ' || (s != end && *s && *s != ' '))                         *p++ = *(s-1);         }         /* wipe out trailing garbage */         while (p != end)                 *p++ = '\0'; } // pobiera fizyczne dane o dysku static void identify_intr(void) {         unsigned int dev = DEVICE_NR(CURRENT->rq_dev);         unsigned short stat = inb_p(HD_STATUS);         struct hd_driveid *id = hd_ident_info[dev];         if (unmask_intr[dev])                 sti();         if (stat & (BUSY_STAT|ERR_STAT)) {                 printk ("  hd%c: non-IDE device, %dMB, CHS=%d/%d/%d\n", dev+'a',                         hd_info[dev].cyl*hd_info[dev].head*hd_info[dev].sect / 2048,                         hd_info[dev].cyl, hd_info[dev].head, hd_info[dev].sect);                 if (id != NULL) {                         hd_ident_info[dev] = NULL;                         kfree_s (id, 512);                 }         } else {                 insw(HD_DATA, id, 256); /* get ID info */                 max_mult[dev] = id->max_multsect;                 if ((id->field_valid&1) && id->cur_cyls && id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) {                         /*                          * Extract the physical drive geometry for our use.                          * Note that we purposely do *not* update the bios_info.                          * This way, programs that use it (like fdisk) will                          * still have the same logical view as the BIOS does,                          * which keeps the partition table from being screwed.                          */                         hd_info[dev].cyl  = id->cur_cyls;                         hd_info[dev].head = id->cur_heads;                         hd_info[dev].sect = id->cur_sectors;                 }                 fixstring (id->serial_no, sizeof(id->serial_no));                 fixstring (id->fw_rev, sizeof(id->fw_rev));                 fixstring (id->model, sizeof(id->model));                 printk ("  hd%c: %.40s, %dMB w/%dKB Cache, CHS=%d/%d/%d, MaxMult=%d\n",                         dev+'a', id->model, id->cyls*id->heads*id->sectors/2048,                         id->buf_size/2, bios_info[dev].cyl, bios_info[dev].head,                         bios_info[dev].sect, id->max_multsect);                 /*                  * Early model Quantum drives go weird at this point,                  *   but doing a recalibrate seems to "fix" them.                  * (Doing a full reset confuses some other model Quantums)                  */                 if (!strncmp(id->model, "QUANTUM", 7))                         special_op[dev] = recalibrate[dev] = 1;         } #if (HD_DELAY > 0)         last_req = read_timer(); #endif         hd_request(); // wywołuje kolejne zadania do wykonania dla dysku         return; } // ustawia liczbę sektorów następnych operacji czytania/pisania static void set_multmode_intr(void) {         unsigned int dev = DEVICE_NR(CURRENT->rq_dev), stat = inb_p(HD_STATUS);         if (unmask_intr[dev])                 sti();         if (stat & (BUSY_STAT|ERR_STAT)) {                 mult_req[dev] = mult_count[dev] = 0;                 dump_status("set multmode failed", stat);         } else {                 if ((mult_count[dev] = mult_req[dev]))                         printk ("  hd%c: enabled %d-sector multiple mode\n",                                 dev+'a', mult_count[dev]);                 else                         printk ("  hd%c: disabled multiple mode\n", dev+'a');         } #if (HD_DELAY > 0)         last_req = read_timer(); #endif         hd_request();         return; } // sprawdza, czy dany dysk jest zajęty i drukuje jego stan gdy to sie stalo static int drive_busy(void) {         unsigned int i;         unsigned char c;         for (i = 0; i < 500000 ; i++) {                 c = inb_p(HD_STATUS); // pobiera stan dysku                 if ((c & (BUSY_STAT | READY_STAT | SEEK_STAT)) == STAT_OK)                         return 0;         }         dump_status("reset timed out", c); // wywala status         return 1; } // resetuje kontroler static void reset_controller(void) {         int     i;         outb_p(4,HD_CMD);         for(i = 0; i < 1000; i++) barrier();         outb_p(hd_info[0].ctl & 0x0f,HD_CMD); // spraawdza czy już wszystko na dysku jest w porządku         for(i = 0; i < 1000; i++) barrier();         if (drive_busy()) // sprawdza czy kontroler jest zajęty                 printk("hd: controller still busy\n");         else if ((hd_error = inb(HD_ERROR)) != 1) // gdy zajenty to wypisuje błąd                 printk("hd: controller reset failed: %02x\n",hd_error); } // resetuje dysk static void reset_hd(void) {         static int i; repeat:         if (reset) { // sprawdza czy należy zresetować                 reset = 0; // ustawia zmienną, żeby tego już nie robić                 i = -1;                 reset_controller(); // resetuje kontroler         } else {                 check_status(); // sprawdza i wypisuje stan dysku                 if (reset)                         goto repeat; // gdy check_status zmieni zmienną reset sytuacja powtarza się. SKOKI w kodzie ąródłowym ???         }         if (++i < NR_HD) { // wykonujemy dla wszystkich dysków w systemie                 special_op[i] = recalibrate[i] = 1; // rekalibrujemy dla każdego dysku                 if (unmask_intr[i]) { // maskujemy przerwania gdy potrzeba                         unmask_intr[i] = DEFAULT_UNMASK_INTR;                         printk("hd%c: reset irq-unmasking to %d\n",i+'a',                                 DEFAULT_UNMASK_INTR);                 }                 if (mult_req[i] || mult_count[i]) { // przywracamy liczbę sektorów do czytania / pisania                         mult_count[i] = 0;                         mult_req[i] = DEFAULT_MULT_COUNT;                         printk("hd%c: reset multiple mode to %d\n",i+'a',                                 DEFAULT_MULT_COUNT);                 } // informujem dysk o zadaniu                 hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,                         hd_info[i].cyl,WIN_SPECIFY,&reset_hd);                 if (reset) // hd_out mogła zmienic wartosc zmiennej                         goto repeat; // gdy to sie stało, to znowu skaczemy!         } else                 hd_request(); // biezemy następne zadanie dla dysku z kolejki } /*  * Ok, don't know what to do with the unexpected interrupts: on some machines  * doing a reset and a retry seems to result in an eternal loop. Right now I  * ignore it, and just set the timeout.  *  * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the  * drive enters "idle", "standby", or "sleep" mode, so if the status looks  * "good", we just ignore the interrupt completely.  */ // nieoczekiwane przerwanie do dysku // gdy od dysku przychodzi przerwanie, a sterownik nie prosił o żadne dane void unexpected_hd_interrupt(void) {         unsigned int stat = inb_p(HD_STATUS);         if (stat & (BUSY_STAT|DRQ_STAT|ECC_STAT|ERR_STAT)) {                 dump_status ("unexpected interrupt", stat); // wypisuje stan dysku                 SET_TIMER;         } } /*  * bad_rw_intr() now tries to be a bit smarter and does things  * according to the error returned by the controller.  * -Mika Liljeberg (liljeber@cs.Helsinki.FI)  */ // funkcja wywoływana w przypadku błędnego przerwania static void bad_rw_intr(void) {         int dev;         if (!CURRENT)                 return;         dev = DEVICE_NR(CURRENT->rq_dev); // ustawia bieżący numer dysku         if (++CURRENT->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {                 end_request(0);                 special_op[dev] = recalibrate[dev] = 1; // ustawia znacznik specjalnych operacji na rekalibrowanie         } else if (CURRENT->errors % RESET_FREQ == 0)                 reset = 1; // jeśli są jakieś błędy to będziemy resetować dysk         else if ((hd_error & TRK0_ERR) || CURRENT->errors % RECAL_FREQ == 0) // rekalibrujemy tak, czy inaczej                 special_op[dev] = recalibrate[dev] = 1;         /* Otherwise just retry */ } // czeka ąż dysk zakończy transmisję static inline int wait_DRQ(void) {         int retries = 100000, stat;         while (--retries > 0)                 if ((stat = inb_p(HD_STATUS)) & DRQ_STAT) // sprawdza, czy transmisja odbywa się nadal                         return 0;         dump_status("wait_DRQ", stat); //wypisuje stan dysku         return -1; } // czyta dane z dysku static void read_intr(void) {         unsigned int dev = DEVICE_NR(CURRENT->rq_dev);         int i, retries = 100000, msect = mult_count[dev], nsect; // msect - ilość danych do przeczytania za jednym podejściem         if (unmask_intr[dev]) // maskuje przerwania podczas transferu danych                 sti();                  /* permit other IRQs during xfer */         do {                 i = (unsigned) inb_p(HD_STATUS);                 if (i & BUSY_STAT)                         continue;                 if (!OK_STATUS(i))                         break;                 if (i & DRQ_STAT) // gdy dysk jest gotowy do transferu                         goto ok_to_read;         } while (--retries > 0); // powtarzamy, bo dysk nie był gotowy do transferu danych, chociaż przyszło od niego przerwanie         dump_status("read_intr", i); // wypisujemy stan dysku         bad_rw_intr(); // oznacza to że nastąpiło nieprawidłowe przerwanie         hd_request(); // bieżemy następne zadanie z kolejki         return; // i kończymy. // sekcja ta wywoływana jest gdy dysk jest gotowy do transferu ok_to_read: // sprawdzamy ile sektorów mamy czytać za jednym razem         if (msect) {                 if ((nsect = CURRENT->current_nr_sectors) > msect)                         nsect = msect;                 msect -= nsect;         } else // gdy nie możemy czytać wielu sektorów na raz, to ustawiamy czytanie tylko jednego                 nsect = 1;         insw(HD_DATA,CURRENT->buffer,nsect<<8); // transfer danych         CURRENT->sector += nsect;         CURRENT->buffer += nsect<<9;         CURRENT->errors = 0;         i = (CURRENT->nr_sectors -= nsect); #ifdef DEBUG         printk("hd%c: read: sectors(%ld-%ld), remaining=%ld, buffer=0x%08lx\n",                 dev+'a', CURRENT->sector, CURRENT->sector+nsect,                 CURRENT->nr_sectors, (unsigned long) CURRENT->buffer+(nsect<<9)); #endif         if ((CURRENT->current_nr_sectors -= nsect) <= 0)                 end_request(1); // kończymy zadanie         if (i > 0) {                 if (msect)                         goto ok_to_read;                 SET_INTR(&read_intr); // ustawiamy funkcje do przerwania powrotnego.                 return;         }         (void) inb_p(HD_STATUS); #if (HD_DELAY > 0)         last_req = read_timer(); // gdy trzeba poczekać na dysk #endif         if (CURRENT)                 hd_request(); // i jak zwykle następne żądanie do dysku         return; } // zapisuje zadaną wielkość sektorów z dysku static inline void multwrite (unsigned int dev) {         unsigned int mcount = mult_count[dev];         while (mcount--) {                 outsw(HD_DATA,WCURRENT.buffer,256); // zapisuje z bufora                 if (!--WCURRENT.nr_sectors)                         return;                 WCURRENT.buffer += 512;                 if (!--WCURRENT.current_nr_sectors) {                         WCURRENT.bh = WCURRENT.bh->b_reqnext;                         if (WCURRENT.bh == NULL)                                 panic("buffer list corrupted\n");                         WCURRENT.current_nr_sectors = WCURRENT.bh->b_size>>9;                         WCURRENT.buffer             = WCURRENT.bh->b_data;                 }         } } // funkcja ustawiająca odpowiednie przerwanie dla pisania wielu sektorów static void multwrite_intr(void) {         int i;         unsigned int dev = DEVICE_NR(WCURRENT.rq_dev);         if (unmask_intr[dev])                 sti(); // maskujemy przerwania         if (OK_STATUS(i=inb_p(HD_STATUS))) { // gdy wszystko z dyskiem w porządku                 if (i & DRQ_STAT) { // gdy dysk transmituje dane                         if (WCURRENT.nr_sectors) { // gdy ustawiona głowica                                 multwrite(dev); // zapisujemy                                 SET_INTR(&multwrite_intr); //ustawiamy funkcie do przerwania                                 return;                         }                 } else { // gdy nic nie transmituje                         if (!WCURRENT.nr_sectors) {     /* all done? */                                 for (i = CURRENT->nr_sectors; i > 0;){                                         i -= CURRENT->current_nr_sectors;                                         end_request(1); // kończymy zadanie                                 } #if (HD_DELAY > 0)                                 last_req = read_timer(); // gdy trzeba to czekamy na dysk #endif                                 if (CURRENT)                                         hd_request(); // bieżemy następne zadanie                                 return; // koniec                         }                 }         } // gdy coś jest nie w porządku z dyskiem         dump_status("multwrite_intr", i); // wypisujemy stan         bad_rw_intr(); // informujemy o złym przerwaniu         hd_request(); // biżemy następne zadanie } // przekazuje dane do zapisania na dysk tylko po jednym sektorze static void write_intr(void) {         int i;         int retries = 100000;         if (unmask_intr[DEVICE_NR(WCURRENT.rq_dev)])                 sti(); // blokuje przerwania         do {                 i = (unsigned) inb_p(HD_STATUS);                 if (i & BUSY_STAT) // powinien dysk być zajęty obsłógą swojego przerwania                         continue;                 if (!OK_STATUS(i)) // gdy błąd to kończymy                         break;                 if ((CURRENT->nr_sectors <= 1) || (i & DRQ_STAT))                         goto ok_to_write; // gdy dysk nie jest gotowy         } while (--retries > 0);         dump_status("write_intr", i); //wypisujemy stan         bad_rw_intr(); // meldujemy o złym przerwaniu         hd_request(); // bieżemy następne żądanie z kolejki         return; // i koniec ok_to_write: // ustalamy pozycję do zapisu         CURRENT->sector++;         i = --CURRENT->nr_sectors;         --CURRENT->current_nr_sectors;         CURRENT->buffer += 512;         if (!i || (CURRENT->bh && !SUBSECTOR(i)))                 end_request(1); // kończymy zadanie jeśli nieodpowiedni sektor         if (i > 0) { // jeśli dysk gotowy                 SET_INTR(&write_intr); // ustawiamy przerwanie powrotne                 outsw(HD_DATA,CURRENT->buffer,256); // zapisujemy                 sti(); // blokujemy przerwania         } else { // gdy dysk nie jest gotowy, trzeba na niebo poczekać #if (HD_DELAY > 0)                 last_req = read_timer(); // czekamy #endif                 hd_request(); // następny z kolejki         }         return; } // sprawdza czy stan dysku jest prawidłowy static void recal_intr(void) {         check_status(); #if (HD_DELAY > 0)         last_req = read_timer(); #endif         hd_request(); } /*  * This is another of the error-routines I don't know what to do with. The  * best idea seems to just set reset, and start all over again.  */ // wywoływana funkcja w przypadku, gdy od dysku nie przychodzi, żadne przerwanie od jakiegoś zbyt długiego czasu static void hd_times_out(void) {         unsigned int dev;         DEVICE_INTR = NULL;         if (!CURRENT)                 return;         disable_irq(HD_IRQ); // wyłączenie wszelkich przerwać do dysku         sti();         reset = 1;         dev = DEVICE_NR(CURRENT->rq_dev);         printk("hd%c: timeout\n", dev+'a');         if (++CURRENT->errors >= MAX_ERRORS) { #ifdef DEBUG                 printk("hd%c: too many errors\n", dev+'a'); #endif                 end_request(0);         }         cli();         hd_request(); // bieżemy następne zadanie, pomijamy aktualnie wykonywane, ponieważ zbyt długo dysk nic nie robił         enable_irq(HD_IRQ); // włączamy obsługę przerwać dla dysku } // odwołanie się do operacji specjalnych na dysku int do_special_op (unsigned int dev) {         if (recalibrate[dev]) { // gdy rekalibrowanie                 recalibrate[dev] = 0;                 hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);                 return reset;         }         if (!identified[dev]) { // gdy brak danych o dysku                 identified[dev]  = 1; // nadajemy identyfikator                 unmask_intr[dev] = DEFAULT_UNMASK_INTR;                 mult_req[dev]    = DEFAULT_MULT_COUNT; // wczytujem dane o dysku                 hd_out(dev,0,0,0,0,WIN_IDENTIFY,&identify_intr);                 return reset; // zwracamy bo mogła się zmienić wartość podczas wykonania hd_out         }         if (mult_req[dev] != mult_count[dev]) { // gdy mamy czytać wiele sektorów                 hd_out(dev,mult_req[dev],0,0,0,WIN_SETMULT,&set_multmode_intr);                 return reset; // mogła być zmiana w hd_out         }         if (hd_info[dev].head > 16) { // dyski o więcej niż 16 głowicach nie są obsługiwane, więc zadanie jest usuwane                 printk ("hd%c: cannot handle device with more than 16 heads - giving up\n", dev+'a');                 end_request(0);         }         special_op[dev] = 0; // koniec wywołania specjalnych operacji         return 1; }
koniec: Mateusz Foks
/*

Autorem poniższej części jest Jakub Husak

*/
/*
 * The driver enables interrupts as much as possible.  In order to do this,
 * (a) the device-interrupt is disabled before entering hd_request(),
 * and (b) the timeout-interrupt is disabled before the sti().
 *
 * Interrupts are still masked (by default) whenever we are exchanging
 * data/cmds with a drive, because some drives seem to have very poor
 * tolerance for latency during I/O.  For devices which don't suffer from
 * that problem (most don't), the unmask_intr[] flag can be set to unmask
 * other interrupts during data/cmd transfers (by defining DEFAULT_UNMASK_INTR
 * to 1, or by using "hdparm -u1 /dev/hd?" from the shell).
 */
/*
 * Sterownik zezwala na przerwania tak często, jak to jest możliwe.Z tego powodu:
 * (a) przerwanie urządzenia jest zablokowane przed wywołaniem hd_request(),
 * (b) przerwanie timeout jest zablokowane przed sti().
 *
 * Przerwania są nadal maskowane (domyślnie) kiedykolwiek zachodzi potrzeba
 * wymiany danych/komend z dyskiem, ponieważ niektóre dyski są bardzo nietolerancyjne
 * jeśli chodzi o trwanie operacji WE/WY.  Dla urządzeń, które są odporne na
 * ten problem (większość jest), flaga unmask_intr[] może być ustawiona, by
 * odblokować pozostałe przewania podczas transferu danych (definiując DEFAULT_UNMASK_INTR
 * na 1, lub wykonując "hdparm -u1 /dev/hd?" z poziomu shella.
 */

/* Procedura strategii sterownika */
static void hd_request(void)
{
        unsigned int dev, block, nsect, sec, track, head, cyl;

        if (CURRENT && CURRENT->rq_status == RQ_INACTIVE) return;
        if (DEVICE_INTR)
                return;
repeat:
        timer_active &= ~(1<<HD_TIMER); /* zerowanie timera */
        sti(); /* zablokowanie przerwań */
        INIT_REQUEST;
        if (reset) {/* jesli zachodzi potrzeba resetu, to resetujemy */
                cli();
                reset_hd();/* i odblokowując przerwania */
                return; /* wracamy */
        }
/* pobieramy urządzenie - jego podrzędny numer */
        dev = MINOR(CURRENT->rq_dev);
        block = CURRENT->sector; /* CURRENT - bierzące żądanie */
        nsect = CURRENT->nr_sectors;
        if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects || ((block+nsect) > hd[dev].nr_sects)) {
#ifdef DEBUG
/* jeśli to nas interesuje, to wypisujemy błędy */
                if (dev >= (NR_HD<<6))
                        printk("hd: bad minor number: device=%s\n",
                               kdevname(CURRENT->rq_dev));
                else
                        printk("hd%c: bad access: block=%d, count=%d\n",
                                (MINOR(CURRENT->rq_dev)>>6)+'a', block, nsect);
#endif
                end_request(0);
                goto repeat;
        }
        block += hd[dev].start_sect;
        dev >>= 6;
/* jeśli trzeba wykonać jakieś specjalne operacje, to */
        if (special_op[dev]) {
/* wykonujemy */
                if (do_special_op(dev))
                        goto repeat;
                return;
        }
/* obliczamy adres na dysku, gdzie będziemy czytać/pisać */ 
        sec   = block % hd_info[dev].sect + 1;
        track = block / hd_info[dev].sect;
        head  = track % hd_info[dev].head;
        cyl   = track / hd_info[dev].head;
#ifdef DEBUG
        printk("hd%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx\n",
                dev+'a', (CURRENT->cmd == READ)?"read":"writ",
                cyl, head, sec, nsect, (unsigned long) CURRENT->buffer);
#endif
        if (!unmask_intr[dev])
                cli();
/* jeśli żądanie dotyczy odczytu */
        if (CURRENT->cmd == READ) {
                unsigned int cmd = mult_count[dev] > 1 ? WIN_MULTREAD : WIN_READ;
/* wywołujemy hd_out z przerwaniem read_intr */
                hd_out(dev,nsect,sec,head,cyl,cmd,&read_intr);
                if (reset)
/* jeśli coś źle - zachodzi potrzeba resetu to */
                        goto repeat;
                return;
        }
/* a jeśli żądanie dotyczy pisania, to */
        if (CURRENT->cmd == WRITE) {
                if (mult_count[dev])
/* jeśli zapisujemy wiele sektorów naraz, to przerwanie multiwrite_inrt
   zadba o prawidłowe obsłużenie żądania */
                        hd_out(dev,nsect,sec,head,cyl,WIN_MULTWRITE,&multwrite_intr);
                else
/* a jeśli nie, to */
                        hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
                if (reset)
/* i znów, jesli zaszła potrzeba resetu dysku , to powtarzamy */
                        goto repeat;
                if (wait_DRQ()) {
                        bad_rw_intr();
                        goto repeat;
                }
                if (mult_count[dev]) {
                        WCURRENT = *CURRENT;
                        multwrite(dev);
                } else
                        outsw(HD_DATA,CURRENT->buffer,256);
                return;
        }
        panic("unknown hd-command");
}

static void do_hd_request (void)
{
        disable_irq(HD_IRQ);
 /* Tutaj wywołujemy hd_request z wyłączonymi przerwaniami */
        hd_request();
        enable_irq(HD_IRQ);
}

static int hd_ioctl(struct inode * inode, struct file * file,
        unsigned int cmd, unsigned long arg)
{
/* argumenty funkcji hd_ioctl:
   struct inode * inode - wskaźnik do i-węzła
   struct file * file
   unsigned int cmd - komenda - patrz niżej instrukcja switch
   unsigned long arg - uniwersalna zmienna przekazująca parametry;
       czasem to będzie wskaźnik, czasem co innego;
*/
/* przekazany wskaźnik do struktury hd_geometry */
        struct hd_geometry *loc = (struct hd_geometry *) arg;
        int dev, err;
        unsigned long flags;
/* jeśli i-węzeł pusty, lub w p. p. jeśli inode->i_rdev pusty */
        if ((!inode) || !(inode->i_rdev))
                return -EINVAL; /* to błąd - zła wartość parametru */
        dev = DEVICE_NR(inode->i_rdev);
        if (dev >= NR_HD) /* jeśli numer dysku zbyt duży - patrz obliczanie NR_HD */
                return -EINVAL; /* błąd - zła wartość parametru */
        switch (cmd) {
                case HDIO_GETGEO:
                /* pobranie geometrii partycji dysku twardego */
                        if (!loc)  return -EINVAL; /* jeśli arg był pusty, to błąd */
                    /* czy można pisać w loc? */
                        err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
                        if (err)
                                return err;
                    /* przepisujemy z BIOSu do naszej struktury loc dane o dysku: */
                    /* ilość głowic - stron na dysku */
                        put_user(bios_info[dev].head,
                                (char *) &loc->heads);
                    /* ilość sektorów na ścieżce */
                       put_user(bios_info[dev].sect,
                                (char *) &loc->sectors);
                    /* cylindrów zajmowanych przez partycję */
                        put_user(bios_info[dev].cyl,
                                (short *) &loc->cylinders);
                    /* startowy sektor partycji */
                        put_user(hd[MINOR(inode->i_rdev)].start_sect,
                                (long *) &loc->start);
                        return 0;
                case BLKRASET:
                /* ustawienie wartości read_ahead dla dysku; argumentem jest i-węzeł
                   z niego wyciągamy numer główny urządzenia - dysku, i dla całego
                   ustawiamy wartość read_ahead
                   tylko dla administratora
                */

                        if(!suser())  return -EACCES; /* jeśli nie administrator - błąd EACCESS*/
                        if(arg > 0xff) return -EINVAL;
                        
                        read_ahead[MAJOR(inode->i_rdev)] = arg;
                        return 0;
                case BLKRAGET:
               /* pobranie wartości read ahead dla dysku */
                        /* tutaj arg jest wskaźnikiem do long int */
                         if (!arg)  return -EINVAL; /* jeżeli wskaźnik NULL to błąd */
                        /* czy można pisać w *arg */
                        err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
                        if (err)
                                return err;
                        /* jeśli tak, to pod arg wpisujemy wartość read_ahead danego dysku*/
                        put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
                        return 0;
                case BLKGETSIZE:   /* Return device size */
               /* pobranie człkowitej ilości sektorów dla danej partycji */
                        if (!arg)  return -EINVAL;
                       /* czy można pisać w *arg */
                        err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
                        if (err)
                                return err;
                        /* jeśli tak, to pod arg wpisujemy wartość nr.sects */
                        put_user(hd[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
                        return 0;
                case BLKFLSBUF:
                /* przy odmontowywaniu dysków trzeba zapisać na nie wszelkie informacje,
                   które są w pamięci, a nie zostały zapisane na dysk.
                   tylko dla administratora
                */
                        if(!suser())  return -EACCES;
                        /* z tego powodu wywołujemy poniższą funkcję */
                        fsync_dev(inode->i_rdev);
                        /* by następnie unieważnić bufory, które zą związane z daną
                        partycją dysku */
                        invalidate_buffers(inode->i_rdev);
                        return 0;

                case BLKRRPART: /* Re-read partition tables */
                        /* czasem zachodzi potrzeba ponownego odczytu tablicy partycji
                           (np. po wymianie dysku)
                           lub po prostu przy starcie systemu wołamy poniższą funkcję; */
                        return revalidate_hddisk(inode->i_rdev, 1);

                case HDIO_SET_UNMASKINTR:
                   /* ta komenda powoduje ustawienie maski dla maskowania przerwań
                      przychodzących z twardego dysku podczas operacji we/wy */
                        if (!suser()) return -EACCES;/* jeśli nie administrator - błąd EACCESS*/
                        if ((arg > 1) || (MINOR(inode->i_rdev) & 0x3F))
                                return -EINVAL;
                        unmask_intr[dev] = arg;
                        return 0;

                case HDIO_GET_UNMASKINTR:
                        /* tu się dowiadujemy, jaka jest maska dla przerwań używana
                           do blokowania przerwań twardego dysku podczas operacji we/wy */
                        if (!arg)  return -EINVAL;/* jeśli arg NULL - błąd EINVAL*/
                        err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
                        if (err)
                                return err;
                        /* wpisz pod *arg wartość unmask_intr[dev] */ 
                        put_user(unmask_intr[dev], (long *) arg);
                        return 0;

                case HDIO_GET_MULTCOUNT:
                        /* wykonując tę komendę dostajemy informację, 
                           ile sektorów można wczytać/zapisać pojedynczą operacją
                           blk_read/blk_write
                        */
                        if (!arg)  return -EINVAL;/* jeśli arg NULL - błąd EINVAL*/
                        /* czy można pisać w *arg */                      
                        err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
                        if (err)
                                return err;
                        /* jeśli tak, to wpisz pod *arg ile sektorów można czytać
                            jedną operacją we/wy */
                        put_user(mult_count[dev], (long *) arg);
                        return 0;

                case HDIO_SET_MULTCOUNT:
                        /* wykonanie tej komendy ustawi ilość
                           sektorów którą można wczytać/zapisać pojedynczą operacją
                           blk_read/blk_write
                           tylko dla administratora
                        */
                        if (!suser()) return -EACCES;/* jeśli nie administrator - błąd EACCESS*/
                        if (MINOR(inode->i_rdev) & 0x3F) return -EINVAL;

                        save_flags(flags);
                        cli();  /* a prior request might still be in progress */
/* ilość sektorów nie może być większa, niż maksymalna */
                        if (arg > max_mult[dev])
                                err = -EINVAL;  /* out of range for device */
                        else if (mult_req[dev] != mult_count[dev]) {
                                special_op[dev] = 1;
                                err = -EBUSY;   /* busy, try again */
                        } else {
/* wszystko ok, ustawiamy nową wartość nadmieniając, że trzeba wykonać do_special_op */
                                mult_req[dev] = arg;
                                special_op[dev] = 1;
                                err = 0;
                        }
                        restore_flags(flags);
                        return err;

                case HDIO_GET_IDENTITY:
/* arg musi być wskaźnikiem */
                        if (!arg)  return -EINVAL;
                        if (MINOR(inode->i_rdev) & 0x3F) return -EINVAL;
/* czy jest co pobierać ? */
                        if (hd_ident_info[dev] == NULL)  return -ENOMSG;
                        /* czy można pisać w *arg */
                        err = verify_area(VERIFY_WRITE, (char *) arg, sizeof(struct hd_driveid));
                        if (err)
                                return err;
                        /* jeśli tak, to kopiujemy pod *arg *hd_ident_info[dev] */
                        memcpy_tofs((char *)arg, (char *) hd_ident_info[dev], sizeof(struct hd_driveid));
                        return 0;

                RO_IOCTLS(inode->i_rdev,arg);
                default:
                        return -EINVAL;
        }
}

/* Funkcja otwiera urządzenie do operacji we/wy */

static int hd_open(struct inode * inode, struct file * filp)
{
        int target;
        target =  DEVICE_NR(inode->i_rdev);

        if (target >= NR_HD)
                return -ENODEV;
        while (busy[target])
                sleep_on(&busy_wait);
        access_count[target]++;
        return 0;
}

/*
 * Releasing a block device means we sync() it, so that it can safely
 * be forgotten about...
 */
/* Funkcja zwalnia (zamyka) urządzenie po operacji we/wy
 * (odwrotna do hd_open() )
 * Zwolnienie urządzenia blokowego polega na sync(), a potem
 * możemy bezpiecznie o nim zapomnieć
 */



static void hd_release(struct inode * inode, struct file * file)
{
        int target;
        /* Zapisujemy wszelkie zmiany */
        sync_dev(inode->i_rdev);

        /* i zmniejszamy licznik dostępu */
        target =  DEVICE_NR(inode->i_rdev);
        access_count[target]--;

}

static void hd_geninit(struct gendisk *);

static struct gendisk hd_gendisk = {
        MAJOR_NR,       /* Major number */      
        "hd",           /* Major name */
        6,              /* Bits to shift to get real from partition */
        1 << 6,         /* Number of partitions per real */
        MAX_HD,         /* maximum number of real */
        hd_geninit,     /* init function */
        hd,             /* hd struct */
        hd_sizes,       /* block sizes */
        0,              /* number */
        (void *) bios_info,     /* internal */
        NULL            /* next */
};
/* Standardowa procedura przerwania twardego dysku */ 
       
static void hd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
        void (*handler)(void) = DEVICE_INTR;

        DEVICE_INTR = NULL;
        timer_active &= ~(1<<HD_TIMER);
        if (!handler)
                handler = unexpected_hd_interrupt;
        handler();
        sti();
}
/*
 * This is the harddisk IRQ description. The SA_INTERRUPT in sa_flags
 * means we run the IRQ-handler with interrupts disabled: this is bad for
 * interrupt latency, but anything else has led to problems on some
 * machines...
 *
 * We enable interrupts in some of the routines after making sure it's
 * safe.
 */

/*
 * To jest opis IRQ dysku. Flaga SA_INTERRUPT w sa_flags
 * znaczy, że używamy handlera IRQ z zablokowanymi przerwaniami: z tego powodu 
 * przerwania dłużej trwają, ale jakiekolwiek inne rozwiązanie może
 * powodować problemy na innych maszynach...
 *
 * Zezwalamy na przerwania w niektórych miejscach, ale wtedy, gdy
 * się upewnimy, że jest to absolutnie bezpieczne
 */
static void hd_geninit(struct gendisk *ignored)
{
        int i;
/* Kod specyficzny dla platformy PC */
#ifdef __i386__
/* Jeśli było zdefiniowane HD_INFO (jako struktura, standardowo nie ma takiej definicji),
 * to NR_HD mówi, o ilości twardych dysków podłączonych do systemu;
 * Jeśli nie, to NR_HD jest równe 0, i stąd trzeba je zainicjować (policzyć dyski, które są
 * co najwyżej 2 (sterownik AT)
*/

        if (!NR_HD) {
                extern struct drive_info drive_info;
                /* dostajemy się do części BIOSu odpowiedzialnej za dyski */
                unsigned char *BIOS = (unsigned char *) &drive_info;
                int cmos_disks, drive;

                /* wypełniamy tablicę (2-elementową) bios_info i hd_info danymi z BIOSu o dyskach */
                for (drive=0 ; drive<2 ; drive++) {
                        bios_info[drive].cyl   = hd_info[drive].cyl = *(unsigned short *) BIOS;
                        bios_info[drive].head  = hd_info[drive].head = *(2+BIOS);
                        bios_info[drive].wpcom = hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
                        bios_info[drive].ctl   = hd_info[drive].ctl = *(8+BIOS);
                        bios_info[drive].lzone = hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
                        bios_info[drive].sect  = hd_info[drive].sect = *(14+BIOS);

#ifdef does_not_work_for_everybody_with_scsi_but_helps_ibm_vp
                /* jeśli dysk istnieje to zwiększamy ilość dysków */
                        if (hd_info[drive].cyl && NR_HD == drive)
                                NR_HD++;
#endif
                        BIOS += 16; /* idziemy do części z danymi o następnym dysku */
                }

        /*
                We query CMOS about hard disks : it could be that 
                we have a SCSI/ESDI/etc controller that is BIOS
                compatible with ST-506, and thus showing up in our
                BIOS table, but not register compatible, and therefore
                not present in CMOS.

                Furthermore, we will assume that our ST-506 drives
                <if any> are the primary drives in the system, and 
                the ones reflected as drive 1 or 2.

                The first drive is stored in the high nibble of CMOS
                byte 0x12, the second in the low nibble.  This will be
                either a 4 bit drive type or 0xf indicating use byte 0x19 
                for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS.

                Needless to say, a non-zero value means we have 
                an AT controller hard disk for that drive.

                
        */
        /*
                pytamy CMOS o twarde dyski : może się zdarzyć, 
                że mamy kontroler SCSI/ESDI/inny kompatybilny 
                na poziomie BIOSu z ST-506, i zawarty z tego powodu w
                tablicy BIOSu ale niekompatybilny na poziomie rejestrów,
                stąd nieistniejący w CMOS.

                Co więcej, zakładamy, że napędy ST-506 , jeżeli są,
                są podstawowymi dyskami w systemie, odwzorowanymi na 
                numery 1 i 2

                Pierwszy dysk zajmuje górną połowę bajtu nr 0x12 CMOS
                drugi dolną. Bajt ten mówi zarówno o 4-bitowych typach dysków
                jak też wartość 0xf tego bajtu mówi o wykorzystaniu bajtu 0x19 
                jako 8-bitowego typu dysku 1, 0x1a dla dysku 2 w CMOS.

                Oczywiście, niezerowa wartość znaczy, że mamy
                kontroler AT dla tego dysku.
        */
        /*      Tutaj liczymy ilość twardych dysków na podstawie powyższych ustaleń,
                jeśli dyski mają czterobitowe typy. Jeśli nie, to jest to już obliczone
        */

                if ((cmos_disks = CMOS_READ(0x12)) & 0xf0)
                        if (cmos_disks & 0x0f)
                                NR_HD = 2;
                        else
                                NR_HD = 1;
        }
#endif /* __i386__ */

        /* dla wszystkich dysków, rozwiązujemy problem zbyt dużej ilości cylindrów */
        i = NR_HD;
        while (i-- > 0) {
                /*
                 * The newer E-IDE BIOSs handle drives larger than 1024
                 * cylinders by increasing the number of logical heads
                 * to keep the number of logical cylinders below the
                 * sacred INT13 limit of 1024 (10 bits).  If that is
                 * what's happening here, we'll find out and correct
                 * it later when "identifying" the drive.
                 */
                /*
                 * Nowsze BIOSy E-IDE obsługują dyski z liczbą cylindrów > 1024
                 * zwiększając liczbę logicznych głowic
                 * aby liczba logicznych cylindrów była poniżej
                 * świętego limitu INT13 równego 1024 cyl. (10 bitów).
                 * Jeśli to się tutaj zdarzy, to rozwiążemy ten problem później,
                 * podczas identyfikacji dysku.
                 */
                hd[i<<6].nr_sects = bios_info[i].head *
                                bios_info[i].sect * bios_info[i].cyl;
                hd_ident_info[i] = (struct hd_driveid *) kmalloc(512,GFP_KERNEL);
                special_op[i] = 1;
        }
        if (NR_HD) {/* jeśli wykryto jakieś dyski */
                if (request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd", NULL)) {
                        printk("hd: unable to get IRQ%d for the harddisk driver\n",HD_IRQ);
                        NR_HD = 0;
                } else {
                        request_region(HD_DATA, 8, "hd");
                        request_region(HD_CMD, 1, "hd(cmd)");
                }
        }
        hd_gendisk.nr_real = NR_HD; /*   tu ustawiamy wcześniej obliczoną ilość dysków */
        /* dla wszystkich partycji wszystkich dysków wstępnie ustawiamy wielkość bloku na 1024 */
        for(i=0;i<(MAX_HD << 6);i++) hd_blocksizes[i] = 1024;
        blksize_size[MAJOR_NR] = hd_blocksizes; 
}

/* tablicz rozdzielcza dla hd */
static struct file_operations hd_fops = {
        NULL,                   /* lseek - default */
        block_read,             /* read - general block-dev read */
        block_write,            /* write - general block-dev write */
        NULL,                   /* readdir - bad */
        NULL,                   /* select */
        hd_ioctl,               /* ioctl */
        NULL,                   /* mmap */
        hd_open,                /* open */
        hd_release,             /* release */
        block_fsync             /* fsync */
};

int hd_init(void)
{
        if (register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
                printk("hd: unable to get major %d for harddisk\n",MAJOR_NR);
                return -1;
        }
        blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
        read_ahead[MAJOR_NR] = 8;               /* 8 sector (4kB) read-ahead */
        hd_gendisk.next = gendisk_head;
        gendisk_head = &hd_gendisk;
        timer_table[HD_TIMER].fn = hd_times_out;
        return 0;
}

#define DEVICE_BUSY busy[target]
#define USAGE access_count[target]
#define CAPACITY (bios_info[target].head*bios_info[target].sect*bios_info[target].cyl)
/* We assume that the the bios parameters do not change, so the disk capacity
   will not change */
#undef MAYBE_REINIT
#define GENDISK_STRUCT hd_gendisk

/*
 * This routine is called to flush all partitions and partition tables
 * for a changed scsi disk, and then re-read the new partition table.
 * If we are revalidating a disk because of a media change, then we
 * enter with usage == 0.  If we are using an ioctl, we automatically have
 * usage == 1 (we need an open channel to use an ioctl :-), so this
 * is our limit.
 */

/* Ta funkcja jest wołana, by "odśmiecić" (flush) wszystkie partycje i tablice partycji
 * dla wymienionego dysku SCSI, po czym odczytać i uaktualnić nową tablicę
 * partycji. Jeśli odświeżamy informacje o dysku z powodu zmiany nośnika,
 * wtedy wołamy revalidate_hddisk z usage równym 0; Jeśli używamy ioctl,
 * automatycznie dostajemy usage równe 1 ( bo potrzebujemy otwarty kanał
 * by użyć ioctl) - to jest nasze ograniczenie.
 */

static int revalidate_hddisk(kdev_t dev, int maxusage)
{/* dev - numer urządzenia */
        int target; /* tu będzie MINOR nr urz. */
        struct gendisk * gdev;/* struktura opisująca urządzenie */
        int max_p; /* tu będzie wpisama maks. ilość partycji urz.*/
        int start;
        int i;
        long flags;
/* DEVICE_NR(dev) - bierzemy MINOR(dev)*/
        target = DEVICE_NR(dev);
        gdev = &GENDISK_STRUCT; /* 

        save_flags(flags);
        cli();
        if (DEVICE_BUSY || USAGE > maxusage) {
                restore_flags(flags);

                return -EBUSY;
        };
        DEVICE_BUSY = 1;
        restore_flags(flags);

        max_p = gdev->max_p; /* maks. ilość partycji dla danego urz. */
        start = target << gdev->minor_shift;

        for (i=max_p - 1; i >=0 ; i--) {
        /* Dla wszystkich partycji robimy , co następuje: */
                int minor = start + i;
                kdev_t devi = MKDEV(MAJOR_NR, minor);
                sync_dev(devi);
                invalidate_inodes(devi);
                invalidate_buffers(devi);
                gdev->part[minor].start_sect = 0;
                gdev->part[minor].nr_sects = 0;
        };

/* MAYBE_REINIT jest niezdefiniowany, ale dla jakichś dziwnych dysków
 * być może trzeba wykonać jakieś operacje */
#ifdef MAYBE_REINIT
        MAYBE_REINIT;
#endif



        /* Inicjalizujemy urządzenie, jego pojemność - ilość sektorów */
        gdev->part[start].nr_sects = CAPACITY;
        /* oraz resetujemy je i ustawiamy, oraz budzimy*/
        resetup_one_dev(gdev, target);

        DEVICE_BUSY = 0;
        wake_up(&busy_wait);
        return 0;
}