Witam
Pracuję jako programista systemów embedded.
Chciałbym dowiedzieć się gdzie mogę znaleźć listing drivera z jądra linuksa powiedzmy do RS232 napisanego w języku C ?
Czy w ogóle drivery jądra linuksowego są oparte o język C ?
Źródła Linuksa są tu: https://github.com/torvalds/linux
Napisany jest w C i szczątkowo w asemblerze.
OK, super, dziękuję Ci.
Programuję AVR'y.
Chciałbym jeszcze prosić o odpowiedź na poniższe:
- załóżmy że chciałbym stworzyć takie oto makro o nazwie LED_ON które by zapalało diodę (czyt. ustawiało pin portu A , powiedzmy PA5 na jeden).
Wiem że mogę to po prostu osiągnąć wpisując :
#define LED_ON PORTA|=(1<<PA5)
z tym że ja wolałbym mieć strukturę z polami bitowymi gdzie każde pole bitowe odnosiłoby się do konkretnego bitu pamięci odpowiadającemu pinowi tego portu.
Wówczas mógłbym np. stworzyć makra tego typu:
#define LED struktura.bit_1
#define ON 1
#define OFF 0
a wywołanie robiłbym wówczas tak:
LED=ON;
LED=OFF;
Pytanie brzmi : czy mógłbym osiągnąć takie coś bez modyfikowania skryptu linkera? Jasne że modyfikując skrypt linkera można sprawić że dana zmienna typu strukturalnego będzie umieszczona w pamięci dokładnie w tym miejscu co dane odpowiadające PORT'owi. Jeśli tylko taka zmienna i pola bitowe tego typu strukturalnego byłyby dodatkowo opatrzone volatile'm to na pewno uzyskałbym taką właśnie funkcjonalność że przy odwołaniu się do pól tej struktury odwoływałbym się już do konkretnego bitu w pamięci RAM.
Troszkę skomplikowałem moje zapytanie za co przepraszam, powinno być:
Czy istnieje jakiś prosty sposób aby zadeklarować strukturę i umieścić ją pod konkretnym adresem w pamięci bez modyfikowania i ingerencji w skrypt linkera ?
Mógłbyś zrobić strukturę, wyciągnąć referencje z PORTA
, scastować i się odwoływać:
typedef struct
{
unsigned bit_0 : 1;
unsigned bit_1 : 1;
unsigned bit_2 : 1;
unsigned bit_3 : 1;
// ...
} bitset_t;
#define LED ((bitset_t *)(&PORTA))->bit_0
#define ON 1
#define OFF 0
void foo(void)
{
LED = ON;
}
Dziękuję za podpowiedź.
Super pomyślane.
Chciałbym tylko jeszcze dopytać teoretycznie jak to jest że takie coś jest w ogóle możliwe...
Trochę łopatologicznie jeśli można prosić:
- Każemy kompilatorowi potraktować (cast) referencję PORTu jako wskaźnik na typ strukturalny.
- Następnie strzałką wyciągamy spod tego wskazania bit o numerze powiedzmy jeden.
I pytanie: Czy to jest to samo co by powiedzieć kompilatorowi w skrócie : Weź potraktuj PORTA w taki sposób jak traktowana jest struktura takiego a takiego typu?
To naprawdę całkiem logiczne i przejrzyste.
Nawet nie wiedziałem że tak można robić.
Czy ktoś próbował użyć takiego zapisu w programie analizując go narzędziami weryfikacji kodu np. Q-AC ?
Nie czepiają się takiego stylu ?
Adamos19 napisał(a):
Weź potraktuj PORTA w taki sposób jak traktowana jest struktura takiego a takiego typu?
Załóżmy, że PORTA
to unsigned
. &PORTA
da nam unsigned *
, i ten wskaźnik sobie castujemy do bitset_t *
. Zobaczmy, że adres PORTA
i adres pierwszego pola w strukturze jest taki sam:
#include <stdio.h>
typedef struct
{
unsigned a;
unsigned b;
} foo;
int main(void) {
unsigned local_a = 0;
foo *f = (foo *)(&local_a);
printf("address local_a: %X, f->a: %X, f->b: %X\n", &local_a, &f->a, &f->b);
f->a = 2; // ok
// f->b = 4; // !!! nadpisanie pamięci, która nie należy do nas
printf("local_a = %u\n", local_a);
return 0;
}
W teorii standard C zakłada ciągłość pamięci, więc powinno wszystko działać.
Problem może nastąpić, kiedy odwołamy się do adresu poza zmienną PORTA
. Wtedy nadpiszemy pamięć innej zmiennej lub stosu (na stosie są odłożone np. adresy powrotu z funkcji, więc nadpisanie może się skończyć crashem programu).
Generalnie aby nie stwarzać zagrożenia, radziłbym zrobić coś takiego:
#include <stdio.h>
unsigned PORTA = 0;
#define OUTPUT PORTA
#define BIT(n) (1 << n)
#define LED(op) (OUTPUT op BIT(4))
#define ON |=
#define OFF &=~
int main(void) {
printf("PORTA = %u\n", PORTA);
LED(ON);
printf("PORTA = %u\n", PORTA);
LED(OFF);
printf("PORTA = %u\n", PORTA);
return 0;
}
Ewentualnie, jeśli już bardzo byś chciał to mieć oparte na strukturze, to wtedy radziłbym zastosować jakiegoś compile-time asserta, np.:
#define STATIC_ASSERT(COND) typedef char static_assertion_##__LINE__[(COND) ? 1 : -1]
#define ENSURE_SIZE(structure, size) STATIC_ASSERT(sizeof(structure) == size)
typedef struct
{
unsigned a;
} foo;
ENSURE_SIZE(foo, sizeof(unsigned)); // ok
typedef struct
{
unsigned a : 1;
unsigned b : 1;
} foo;
ENSURE_SIZE(foo, sizeof(unsigned)); // ok
http://ideone.com/4SxD9F
http://ideone.com/jceZ7h <- 32 bity
http://ideone.com/VsjbQ9 <- 33 bity
typedef struct
{
unsigned a;
unsigned b;
} foo;
ENSURE_SIZE(foo, sizeof(unsigned)); // błąd kompilacji
Dziękuję ponownie.
Co do rzutowania wskaźnika to mamy jasność. Wskaźnik możemy castować na wskaźnik danego typu strukturalnego i później odwoływać się do pól takiej struktury tym samym odwołując się de facto do konkretnych bitów w pamięci. Należy przy tym pamiętać aby ten typ strukturalny był tak utworzony aby nie było możliwości nadpisania innych struktur pamięci ponieważ bardzo łatwo tutaj popełnić błąd co zresztą super pokazałeś na przykładzie.
Jednakże problem sprawia mi to co napisałeś od tego momentu :
Ewentualnie, jeśli już bardzo byś chciał to mieć oparte na strukturze, to wtedy radziłbym zastosować jakiegoś compile-time asserta...
Cóż to takiego za cudo ten "compile-assert"?