SmartGWT: zamknięcie okna po operacji saveData()

, niedziela, 31 lipca 2011 0 komentarze

Przedstawiam rozwiązanie problemu dotyczącego zamknięcia okna po poprawnym wykonaniu operacji save. Problem polega na tym, że po wykonaniu kodu jak poniżej dostaniemy błąd NULL-owej referencji do okna.

public class DynamicFormWindow extends Window {
protected DynamicForm form;
private ActionPanel actionPanel = new ActionPanel();

public DynamicFormWindow(DynamicForm form, String title) {
this.form = form;

setShowShadow(true);
setShadowSoftness(4);
setShadowOffset(3);

this.setTitle(title);
this.setLayoutMargin(4);
this.setShowMinimizeButton(false);
this.setCanDragResize(true);
this.setAutoSize(true);
this.setIsModal(true);
this.setShowModalMask(false);
this.centerInPage();

this.addCloseClickHandler(new CloseClickHandler() {
@Override
public void onCloseClick(CloseClientEvent event) {
destroy();
}
});

VPanel panel = new VPanel();
panel.setPadding(2);
panel.addMember(form);
panel.addMember(this.actionPanel);

this.addItem(panel);

IButton saveButton = new IButton("Zapisz");
saveButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
DynamicFormWindow.this.saveData();
}
});
addActionButton(saveButton);
}

protected void saveData() {
this.form.saveData();
this.destroy();
}

protected void addActionButton(IButton button) {
this.actionPanel.addMember(button);
}

}


Problem polega na tym, że po wykonaniu saveData() automatycznie wykonywane jest destroy(), gdzie saveData() wykonuje akcję asynchronicznie używając GWT-RPC po stronie serwerowej aplikacji.

Rozwiązaniem problemu jest wykorzystanie Callbacka na saveData() oraz funkcjonalności Scheduler-a. W tym celu zmieniamy ciało metody saveData() w następujący sposób:


protected void saveData() {
if (this.form.validate()) {
this.form.saveData(new DSCallback() {
@Override
public void execute(DSResponse response, Object rawData, DSRequest request) {
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
DynamicFormWindow.this.destroy();
}
});
}
});
}
}


Takie podejście daje nam to, że kod zamykający okno wykonywany jest po tym jak zakończy się operacja saveData() oraz bieżąca pętla obsługi zdarzeń GWT. Oznacza to, że kod zamykający okno wykona się po całkowitym zakończeniu obsługi związanej z akcją saveData().

Dodatkowe metody Schedulera scheduleEntry/scheduleFinally wykonywane są wewnątrz pętli obsługi zdarzeń zaraz przed/po tym jak wykonany zostanie kod związany z obsługą zdarzeń GWT. W przypadku wykonywania manipulacji DOM wewnątrz tej samej pętli obsługi zdarzeń, można przeciwdziałać dodatkowemu odmalowaniu przez browser.

Web Project Template, czyli Eclipse + Maven+ Spring + GWT + Hibernate

, , , , 0 komentarze

W tym artykule chciałbym podzielić się swoim doświadczeniem w ramach konfiguracji projektu typu Web wykorzystującego Spring, SmartGWT, Hiberante. Całość konfiguracji w zamierzeniu ma być budowana przez Maven-a. Zakładam, że do developmentu używane będzie środowisko Eclipse Helios SR 2. Dlatego zacznijmy od konfiguracji środowiska projektowego.

Zaczynamy od ściągnięcia Eclipsa ze strony Eclipse Helios SR2 Packages (v 3.6.2). Teraz, żeby nam się dobrze pracowało instalujemy następujące pluginy:
- Maven Integration for Eclipse Update Site
- Google Update Site for Eclipse 3.6
- GWT Designer Update Site
- SpringSource Update Site for Eclipse 3.6
- SpringSource Update Site for Eclipse 3.6 (Dependencies)
- JBoss Tools Development Milestone Update Site
Po instalacji Eclipse oraz wymienionych pluginów jesteśmy przygotowani do pracy, więc możemy zacząć. Moja propozycja podziału na moduły aplikacji typu Web jest następująca:
- moduł dziedzinowy: zawierający zarówno model dziedzinowy jak i operacje związane z modelem, czyli inaczej mówiąc model JPA oraz związane z nimi DAO (Data Access Objects)
- moduł usług: zawierający bean-y Springowe udostępniające usługi biznesowe w oparciu o model dziedzinowy
- moduł warstwy web: moduł zawierający całość logiki klienckiej, udostępniający warstwę UI dla użytkownika końcowego.

Moduł warstwy web komunikuje się z modułem serwisów w celu wykonywania akcji po stronie serwera. Nie ma on dostępu do modułu domenowego, gdyż moduł serwisów izoluje dostęp do warstwy domenowej.

Zajmijmy się teraz konfiguracją modułów. W tym celu utworzyć należy strukturę plików tak jak zostało to przedstawione na rysunku obok.

Najważniejsze w tym momencie są pliki pom.xml, czyli pliki konfiguracyjne Mavena. Teraz zajmę się ustawieniami w tych plikach. Dobrze, główny plik konfiguracyjny (root) znajdujący się na poziomie katalogów m2-domain, m2-service, m2-web wygląda następująco:

  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0
pl.masl.m2
m2-root
1.0.0-SNAPSHOT
pom
M-2 :: ${project.artifactId}


UTF-8
1.6
1.6
4.8.2
3.0.5.RELEASE
3.0.5.RELEASE
3.5.6-Final
1.0.0.Final
2.0.8
5.1.10



m2-domain
m2-service
m2-web




junit
junit
${junit.verion}
test


org.slf4j
jcl-over-slf4j
1.5.8


org.slf4j
slf4j-log4j12
1.5.8





maven-annotation-plugin

http://maven-annotation-plugin.googlecode.com/svn/trunk/mavenrepo






Central Repo
http://repo1.maven.org/maven2


com.springsource.repository.maven.release
http://maven.springframework.org/release/

false



com.springsource.repository.maven.snapshot
http://maven.springframework.org/snapshot/

true



com.springsource.repository.maven.milestone
http://maven.springframework.org/milestone/

false



SmartGWT Repo
http://www.smartclient.com/maven2/


JavaNet
http://download.java.net/maven/2




Co widać w tym pliku to ustawienia dotyczące wersji bibliotek używanych w ramach aplikacji, definicję modułów z których składa się aplikacja oraz definicja repozytoriów dla bibliotek.

Następnie konfigurujemy plik pom.xml dla modułu domenowego:
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0


m2-root
pl.masl.m2
1.0.0-SNAPSHOT


pl.masl.m2
m2-domain
1.0.0-SNAPSHOT
M-2 :: ${project.artifactId}



org.hibernate
hibernate-jpamodelgen
${hibernate-jpa.version}


org.hibernate
hibernate-entitymanager
${hibernate.version}


org.hibernate
hibernate-c3p0
${hibernate.version}


org.hibernate
hibernate-ehcache
${hibernate.version}


org.springframework
spring-test
${spring.version}


org.springframework.data
spring-data-jpa
1.0.0.M2


org.springframework
spring-orm
${spring.version}


mysql
mysql-connector-java
${mysql.connector.version}






org.apache.maven.plugins
maven-compiler-plugin
2.0.2

${maven.compiler.source}
${maven.compiler.target}
${project.build.sourceEncoding}
-proc:none




org.bsc.maven
maven-processor-plugin
1.3.1


process

process

generate-sources

target/metamodel


org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor








org.codehaus.mojo
build-helper-maven-plugin
1.3


add-source
generate-sources

add-source



target/metamodel










Co mamy w tym pliku to zależności od hibernate-a i Springa, definicja pluginu do kompilacji, definicja pluginów do generacji modelu JPA 2.0.

Teraz konfiguracja pliku pom.xml dla modułu usług:
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0

m2-root
pl.masl.m2
1.0.0-SNAPSHOT

pl.masl.m2
m2-service
1.0.0-SNAPSHOT
M-2 :: ${project.artifactId}



pl.masl.m2
m2-domain
1.0.0-SNAPSHOT
jar
compile


aspectj
aspectjrt
1.5.4


aspectj
aspectjweaver
1.5.4


net.sf.dozer
dozer
5.3.2


org.springframework
spring-tx
${spring.version}


org.springframework.security
spring-security-config
${springframework.security.version}






org.apache.maven.plugins
maven-compiler-plugin
2.3.2

${maven.compiler.source}
${maven.compiler.target}
${project.build.sourceEncoding}



org.apache.maven.plugins
maven-source-plugin
2.1.2


attach-sources
verify

jar-no-fork








W tym pliku widać definicję zależności od modułu domenowego, bibliotek Springa oraz pluginów do kompilacji.

No i na koniec definicja pom.xml-a dla modułu webowego:

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">


m2-root
pl.masl.m2
1.0.0-SNAPSHOT


4.0.0
pl.masl.m2
m2-web
war
1.0.0-SNAPSHOT
M-2 :: ${project.artifactId}



2.3.0
2.4
${project.build.directory}/${project.build.finalName}




pl.masl.m2
m2-service
1.0.0-SNAPSHOT


com.google.gwt
gwt-user
${gwtVersion}
provided


com.google.gwt
gwt-servlet
${gwtVersion}
compile


javax.validation
validation-api
1.0.0.GA
test


javax.validation
validation-api
1.0.0.GA
sources
test


com.smartgwt
smartgwt
${smartgwt.version}
compile


com.smartgwt
smartgwt-skins
${smartgwt.version}
compile


com.google.code
gwt-log
3.1.3


net.sf.gwt-widget
gwt-sl
1.3-RC1
jar
compile


gwt-servlet
com.google.gwt


cglib-nodep
cglib




org.springframework
spring-web
${spring.version}


org.springframework
spring-webmvc
${spring.version}



org.springframework.security
spring-security-config
${springframework.security.version}


org.springframework.security
spring-security-web
${springframework.security.version}




${webappDirectory}/WEB-INF/classes
m2



org.codehaus.mojo
gwt-maven-plugin
2.3.0-1



compile
test
i18n
generateAsync





pl.masl.m2:m2-service

M2.html
${webappDirectory}
pl.masl.m2.web.gwt.client.Constants


pl.masl.m2.web.gwt.client.Messages




org.apache.maven.plugins
maven-war-plugin
2.1.1


compile

exploded




${webappDirectory}



org.apache.maven.plugins
maven-compiler-plugin
2.3.2

${maven.compiler.source}
${maven.compiler.target}
${project.build.sourceEncoding}






Tutaj mamy uzależnienie od bibliotek Springa, bibliotek GWT, bibliotek SmartGWT, ustawień dla kompilacji GWT oraz ustawień dla modułu typu WEB.

Teraz zajmiemy się wciągnięciem modułów do Eclipsa.

Obsługa DTO dla Spring i SmartGWT

, , 0 komentarze

Ostatnio miałem problem z przekazywaniem informacji pomiędzy częścią serwerową aplikacji opartą o serwisy w postaci bean-ów Spring oraz częścią kliencką przygotowaną w formie aplikacji SmartGWT. Problem polegał na tym, że miałem dosyć dużo obiektów domenowych JPA i żeby wymieniać informacje z aplikacją kliencką przy standardowym podejściu musiałbym napisać dla każdej klasy obiektu domenowego klasę DTO. Stwierdziłem, że to strasznie dużo roboty i zacząłem się zastanawiać jak sobie ułatwić życie i zrobić to jakimś automatem. Wpadłem na pomysł przesyłania informacji w postaci tzw. dynamic property i zrobiłem pod to implementację, którą chciałbym się w tym momencie podzielić.

Pomysł polega na tym, aby zarówno po stronie serwerowej jak i klienckiej napisać serializer i deserailzer dynamic property. Rozpocząłem od napisania adnotacji, która ma zasięg metody i służyć będzie do oznaczenie getterów dla property przesyłanych na stronę serwerową i na odwrót. Adnotacja taka ma następującą postać:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DynamicDataProperty {

boolean embeded() default false;
}


No dobrze mamy adnotację to teraz oznaczmy przy jej użyciu odpowiednie obiekty domenowe. Załóżmy, że mamy obiekt domenowy Customer, który wykorzystuje zanurzony obiekt Address tak jak poniżej.

@javax.persistence.Entity
@Table(name = "customers")
public class Customer extends EntityObject {
private static final long serialVersionUID = -5104067194504977189L;

@Column(name = "code", unique = true, nullable = false, length = 60)
private String code;

@Column(name = "name", unique = false, nullable = false, length = 255)
private String name;

@Column(name = "discount", unique = false, nullable = false)
private short discount;

@Embedded
private Address address = new Address();

@DynamicDataProperty
public String getCode() {
return this.code;
}

public void setCode(String code) {
this.code = code;
}

@DynamicDataProperty
public String getName() {
return this.name;
}

public void setName(String name) {
this.name = name;
}

@DynamicDataProperty
public short getDiscount() {
return this.discount;
}

public void setDiscount(short discount) {
this.discount = discount;
}

@DynamicDataProperty(embeded = true)
public Address getAddress() {
return this.address;
}

}



@javax.persistence.Embeddable
public class Address implements Serializable {
private static final long serialVersionUID = -2023844913320728453L;

@Column(name = "city", length = 60)
private String city;

@Column(name = "postal_code", length = 10)
private String postalCode;

@Column(name = "street")
private String street;

@Column(name = "local", length = 10)
private String local;

public String getCityPart() {
StringBuffer bf = new StringBuffer();
if (this.postalCode != null) {
bf.append(this.postalCode);
bf.append(" ");
}
if (this.city != null) {
bf.append(this.city);
}
return bf.toString();
}

public String getStreetPart() {
StringBuffer bf = new StringBuffer();
if (this.street != null) {
bf.append(this.street);
}
if (this.local != null) {
bf.append(" ");
bf.append(this.local);
}
return bf.toString();
}

@DynamicDataProperty
public String getFull() {
StringBuffer bf = new StringBuffer();
bf.append(getStreetPart());
if (bf.length() > 0) {
bf.append(", ");
}
bf.append(getCityPart());
return bf.toString();
}

@DynamicDataProperty
public String getCity() {
return this.city;
}

public void setCity(String city) {
this.city = city;
}

@DynamicDataProperty
public String getPostalCode() {
return this.postalCode;
}

public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}

@DynamicDataProperty
public String getStreet() {
return this.street;
}

public void setStreet(String street) {
this.street = street;
}

@DynamicDataProperty
public String getLocal() {
return this.local;
}

public void setLocal(String local) {
this.local = local;
}

}



Jak widać wszystkie gettery zarówno w klasie Customer jak i Address zostały oznaczone adnotacją co powoduje, że wszystkie propery będą przekazywane pomiędzy częścią serwerową i kliencką aplikacji. Dodatkowo getAddress() został oznaczony adnotacją z ustawieniem embeded = true co oznacza, że property dotyczy obiektu zanurzonego, lub inaczej mówiąc obiektu którego serializacja i deserializacja powinna dotyczyć w sensie serializacji jego property.

Teraz tworzymy dwie klasy obiekty których będą przesyłane pomiędzy częścią serwerową i kliencką. Są to klasy do przesyłania zserializowanej postaci obiektów domenowych, czyli swojego rodzaju generyczne DTO. Tak więc mamy klasę konetenerową:

public class DynamicData extends AbstractData {
private static final long serialVersionUID = -2549646020563019965L;

private LinkedHashMap map = new LinkedHashMap();

public LinkedHashMap getMap() {
return this.map;
}

@Override
public Long getId() {
DataProperty prop = this.map.get("id");
return (Long) (prop != null ? prop.getValue() : null);
}
}


i klasę komponentową:

public class DataProperty implements Serializable {
private static final long serialVersionUID = -4627984997682982978L;

private DataPropertyType type = DataPropertyType.STRING;
private String str;
private Integer integer;
private Short shortv;
private Long longv;
private Boolean boolv;
private DynamicData embebed;

public DataProperty() {
this("");
}

public DataProperty(Object value) {
if (value == null) {
throw new IllegalArgumentException("Value cannot be null");
}
if (value instanceof String) {
this.str = (String) value;
} else if ((value instanceof DynamicData)) {
this.embebed = (DynamicData) value;
this.type = DataPropertyType.EMBEDED;
} else if ((value instanceof Boolean)) {
this.boolv = (Boolean) value;
this.type = DataPropertyType.BOOL;
} else if ((value instanceof Short)) {
this.shortv = ((Short) value);
this.type = DataPropertyType.SHORT;
} else if ((value instanceof Integer)) {
this.integer = (Integer) value;
this.type = DataPropertyType.INT;
} else if (value instanceof Long) {
this.longv = (Long) value;
this.type = DataPropertyType.LONG;
} else {
this.str = value.toString();
}
}

public Object getValue() {
switch (this.type) {
case EMBEDED:
return this.embebed;
case BOOL:
return this.boolv;
case SHORT:
return this.shortv;
case INT:
return this.integer;
case LONG:
return this.longv;
default:
return this.str;
}
}

public DataPropertyType getType() {
return this.type;
}
}


public enum DataPropertyType implements Serializable {

STRING,
INT,
LONG,
BOOL,
EMBEDED,
SHORT;
}



Jak widać w klasie komponentowej każdy typ ma swoje pole. Można by zapytać dlaczego nie zrobić jednego pola typu Object, w którym zapisana była by informacja o zserializowanej wartości. Rzeczywiście tak by było najprościej, ale niestety jest to problematyczne z racji wykorzystania GWT. Ponieważ obiekty komponentowe będą przesyłane na stronę kliencką muszą być skompilowane przez GWT do javasrcipt-u i jeżeli damy Obiect kompilator będzie próbował stworzyć generyczny javascript i tu dostaniemy albo warningi przy kompilacji albo problemy przy serializacji w runtime. Dlatego najlepiej jest wyręczyć kompilator GWT i samemu przygotować implementację opierająca się o jasno określone typy serializowanych wartości.

No dobrze mamy klasy domenowe, mamy komponenty umożliwiające nam przekazywanie informacji pomiędzy częścią serwerową i kliencką teraz potrzebujemy serializatorów. Serializator po stronie klienckiej ma następującą postać:

public class DynamicDataMapper {
private static final Logger log = LoggerFactory.getLogger(DynamicDataMapper.class);

private DynamicDataMapper() {
}

public static void map(DynamicData data, T entity) {
if ((data != null) && (entity != null)) {
try {
to(data, entity);
} catch (Exception e) {
throw new MappingException(e);
}
}
}

public static DynamicData map(T entity) {
DynamicData data = null;
if (entity != null) {
data = new DynamicData();
try {
from(data, entity);
} catch (Exception e) {
throw new MappingException(e);
}
}
return data;
}

private static void to(DynamicData data, Object inst) throws Exception {
PropertyDescriptor[] desc = ReflectUtils.getBeanProperties(inst.getClass());
Map propMap = new HashMap();
for (PropertyDescriptor d : desc) {
propMap.put(d.getName(), d);
}
LinkedHashMap map = data.getMap();
for (Entry entry : map.entrySet()) {
String key = entry.getKey();
try {
DataProperty prop = entry.getValue();
PropertyDescriptor d = propMap.get(key);
if (d != null) {
Object value = prop != null ? prop.getValue() : null;
//log.debug("PROP: {}, VALUE: {}", key, value);
if (value instanceof DynamicData) {
Method mth = d.getReadMethod();
if (mth != null) {
Object bean = mth.invoke(inst, new Object[0]);
if (bean != null) {
to((DynamicData) value, bean);
}
}
} else {
Method mth = d.getWriteMethod();
if (mth != null) {
//Class clas = mth.getParameterTypes()[0];
//log.debug("Parameter class: {}", clas);
mth.invoke(inst, value);
}
}
}
} catch (Exception ex) {
throw new MappingException("Error for Parameter " + key, ex);
}
}
}

private static void from(DynamicData data, Object inst) throws Exception {
PropertyDescriptor[] desc = ReflectUtils.getBeanProperties(inst.getClass());
for (PropertyDescriptor des : desc) {
Method mth = des.getReadMethod();
if (mth != null) {
DynamicDataProperty an = mth.getAnnotation(DynamicDataProperty.class);
if (an != null) {
String key = des.getName();
Object result = mth.invoke(inst, new Object[0]);
DataProperty prop = null;
if (result != null) {
if (an.embeded()) {
DynamicData embeded = new DynamicData();
from(embeded, result);
result = embeded;
}
prop = new DataProperty(result);
}
data.getMap().put(key, prop);
}
}
}
}

}


Serializator ma dwie metody statyczne from i to służące do serializacji i deserializacji przekazywanych komponentów. Serializacja polega na zapisaniu property z obiektu domenowego w postaci mapy property, gdzie kluczem jest nazwa a wartością obiekt komponentowy DataProperty zawierający wartość odczytaną z obiektu domenowego. W przypadku property typu embeded tworzony nowy obiekt DynamicData i zapisywany jako wartość w odpowiednim DataProperty.

No dobrze mamy część serwerową no to czas na część kliencką i oto i ona:

public class DynamicDataSource extends TableDataSource {

private static final Converter CONVERTER = new Converter();

public DynamicDataSource(DataServiceAsync service, SortSpecifier[] sort) {
super(service, sort);
}

public DynamicDataSource(DataServiceAsync service) {
super(service);
}

@Override
public IConverter getRecordConverter() {
return CONVERTER;
}

static class Converter implements IConverter {

@Override
public void convertData(Record rec, DynamicData element) {
this.convertData(rec, null, element);
}

@Override
public DynamicData convertRecord(Record rec) {
DynamicData data = new DynamicData();
String[] attrs = rec.getAttributes();
for (String attr : attrs) {
if (Log.isDebugEnabled()) {
Log.debug("Processing attribute: " + attr);
}
LinkedHashMap map = getMap(attr, data);
Object value = rec.getAttributeAsObject(attr);
int idx = attr.lastIndexOf('.');
if (idx > -1) {
attr = attr.substring(idx + 1);
}
DataProperty dp = null;
if (value != null) {
if (value instanceof JavaScriptObject) {
value = JSOHelper.convertToJava((JavaScriptObject) value);
}
if (Log.isDebugEnabled()) {
Log.debug("VALUE calss: " + value.getClass() + " - " + value);
}
dp = new DataProperty(value);
}
map.put(attr, dp);
}
return data;
}

private LinkedHashMap getMap(String attr, DynamicData data) {
LinkedHashMap map = data.getMap();
int idx = attr.indexOf('.');
if (idx > -1) {
String at = attr.substring(0, idx);
DataProperty dp = map.get(at);
if (dp == null) {
DynamicData md = new DynamicData();
dp = new DataProperty(md);
map.put(at, dp);
}
return getMap(attr.substring(idx + 1), (DynamicData) dp.getValue());
}
return map;
}

private void convertData(Record rec, String prop, DynamicData element) {
LinkedHashMap map = element.getMap();
for (Entry entry : map.entrySet()) {
Object value = null;
String property = prop != null ? prop + "." + entry.getKey() : entry.getKey();
DataProperty dp = entry.getValue();
if (dp != null) {
value = dp.getValue();
}
if (value instanceof DynamicData) {
convertData(rec, property, (DynamicData) value);
} else {
rec.setAttribute(property, value);
}
}
}

}
}


Część kliencka jest w postaci DataSource i mamy tutaj metodę convertData() która służy do konwersji obiektu generycznego DTO na atrybuty w rekordzie DataSource-a oraz metodę convertRecord(), która konwertuje dane z rekordu na odpowiedni obiekt generyczny DTO.

Lunux - karta TV: przydatne komendy

, środa, 9 marca 2011 0 komentarze

Pokaż załadowane moduły ograniczając się do cx

sudo lsmod | grep cx


cx8800 29418 0
cx88xx 74190 1 cx8800
rc_core 17007 7 ir_lirc_codec,ir_sony_decoder,ir_jvc_decoder,ir_rc6_decoder,ir_rc5_decoder,ir_nec_decoder,cx88xx
i2c_algo_bit 5205 1 cx88xx
tveeprom 13145 1 cx88xx
v4l2_common 8312 3 tuner,cx8800,cx88xx
videodev 69118 4 tuner,cx8800,cx88xx,v4l2_common
videobuf_dma_sg 8668 2 cx8800,cx88xx
videobuf_core 16044 3 cx8800,cx88xx,videobuf_dma_sg
btcx_risc 3738 2 cx8800,cx88xx
i2c_core 27089 11 tuner_simple,tea5767,nvidia,tuner,cx8800,cx88xx,i2c_algo_bit,tveeprom,v4l2_common,videodev,i2c_i801


Wyświetl bufor warstwy jądra ograniczając się do wpisów zawierających cx
dmesg | grep cx


[ 11.199826] cx88/0: cx2388x v4l2 driver version 0.0.8 loaded
[ 11.199872] cx8800 0000:05:02.0: PCI INT A -> GSI 18 (level, low) -> IRQ 18
[ 11.202933] cx88[0]: subsystem: 0000:0000, board: PixelView [card=3,insmod option], frontend(s): 0
[ 11.202937] cx88[0]: TV tuner type 23, Radio tuner type 23
[ 11.202939] cx88[0]: cx88_reset
[ 11.314193] cx88[0]: Test OK
[ 11.392886] cx88[0]: i2c scan: found device @ 0xc0 [tuner (analog)]
[ 12.606248] tuner 1-0060: chip found @ 0xc0 (cx88[0])
[ 12.641231] cx88[0]/0: found at 0000:05:02.0, rev: 3, irq: 18, latency: 32, mmio: 0xf9000000
[ 12.641671] cx88[0]/0: registered device video0 [v4l2]
[ 12.641883] cx88[0]/0: registered device vbi0
[ 12.642005] cx88[0]/0: registered device radio0
[ 12.642052] cx88[0]: set_tvnorm: "NTSC-M" fsc8=28636360 adc=28636363 vdec=28636360 db/dr=28636360/28636360
[ 12.642058] cx88[0]: set_pll: MO_PLL_REG 0x00fffffe [old=0x00f15f18,freq=28636360]
[ 12.642063] cx88[0]: pll locked [pre=2,ofreq=28636360]
[ 12.642067] cx88[0]: set_tvnorm: MO_INPUT_FORMAT 0x00000001 [old=0x00000007]
[ 12.642073] cx88[0]: set_tvnorm: MO_OUTPUT_FORMAT 0x181f0008 [old=0x181f0000]
[ 12.642078] cx88[0]: set_tvnorm: MO_SCONV_REG 0x00020000 [old=0x00021f07]
[ 12.642082] cx88[0]: set_tvnorm: MO_SUB_STEP 0x00400000 [old=0x0043e0f8]
[ 12.642087] cx88[0]: set_tvnorm: MO_SUB_STEP_DR 0x00400000 [old=0x00538e38]
[ 12.642092] cx88[0]: set_tvnorm: MO_AGC_BURST 0x00007270 [old=0x00006d63,bdelay=114,agcdelay=112]
[ 12.642097] cx88[0]: set_tvnorm: MO_HTOTAL 0x0000038e [old=0x0000135a,htotal=910]
[ 12.642100] cx88[0]: set_scale: 320x240 [TB,NTSC-M]
[ 12.642103] cx88[0]: set_scale: hdelay 0x0038 (width 754)
[ 12.642105] cx88[0]: set_scale: hscale 0x15b3
[ 12.642107] cx88[0]: set_scale: hactive 0x0140
[ 12.642109] cx88[0]: set_scale: vdelay 0x0018
[ 12.642111] cx88[0]: set_scale: vscale 0x1e00
[ 12.642113] cx88[0]: set_scale: vactive 0x01e0
[ 12.642115] cx88[0]: set_scale: filter 0x80009
[ 12.642121] cx88[0]/0: set_audio_standard_BTSC (status: known-good)
[ 12.643011] cx88[0]/0: set_control id=0x980900(Brightness) ctrl=0x7f, reg=0x310110 val=0xff (mask 0xff)
[ 12.643056] cx88[0]/0: set_control id=0x980901(Contrast) ctrl=0x3f, reg=0x310110 val=0x3f00 (mask 0xff00)
[ 12.643063] cx88[0]/0: set_control id=0x980903(Hue) ctrl=0x7f, reg=0x310118 val=0xff (mask 0xff)
[ 12.643069] cx88[0]/0: set_control id=0x980902(Saturation) ctrl=0x7f, reg=0x310114 val=0x5a7f (mask 0xffff)
[ 12.643074] cx88[0]/0: set_control id=0x98091D(Chroma AGC) ctrl=0x01, reg=0x310104 val=0x400 (mask 0x400)
[ 12.643079] cx88[0]/0: set_control id=0x98091E(Color killer) ctrl=0x01, reg=0x310104 val=0x200 (mask 0x200)
[ 12.643084] cx88[0]/0: set_control id=0x980909(Mute) ctrl=0x01, reg=0x320594 val=0x40 (mask 0x40) [shadowed]
[ 12.643088] cx88[0]/0: set_control id=0x980905(Volume) ctrl=0x3f, reg=0x320594 val=0x00 (mask 0x3f) [shadowed]
[ 12.643092] cx88[0]/0: set_control id=0x980906(Balance) ctrl=0x40, reg=0x320598 val=0x00 (mask 0x7f) [shadowed]
[ 12.643096] cx88[0]/0: video_mux: 0 [vmux=0,gpio=0xff00,0x0,0x0,0x0]
[ 12.647184] cx88[0]/0: cx88: tvaudio thread started
[ 12.664972] cx88[0]/0: open dev=vbi0 radio=0 type=vbi-cap
[ 12.665165] cx88[0]/0: open dev=radio0 radio=1 type=(null)
[ 12.665169] cx88[0]/0: video_open: setting radio device
[ 12.665173] cx88[0]/0: set_audio_standard_FM (status: unknown)
[ 12.665197] cx88[0]/0: cx88_set_stereo: mask 0x3f, ctl 0x1a [status=0x22,ctl=0x101a,vol=0x40]
[ 12.666213] cx88[0]/0: open dev=video0 radio=0 type=vid-cap
[ 27.217735] cx88[0]/0: open dev=radio0 radio=1 type=(null)
[ 27.217742] cx88[0]/0: video_open: setting radio device
[ 27.217745] cx88[0]/0: set_audio_standard_FM (status: unknown)
[ 27.217769] cx88[0]/0: cx88_set_stereo: mask 0x3f, ctl 0x1a [status=0x22,ctl=0x101a,vol=0x40]
[ 27.220711] cx88[0]/0: open dev=vbi0 radio=0 type=vbi-cap
[ 27.222937] cx88[0]/0: open dev=video0 radio=0 type=vid-cap
[ 239.487766] cx88[0]/0: open dev=video0 radio=0 type=vid-cap


Wyświetl karty dźwiękowe
cat /proc/asound/cards


0 [Intel ]: HDA-Intel - HDA Intel
HDA Intel at 0xfb000000 irq 45
1 [Audigy ]: Audigy - SB Audigy 1 [SB0092]
SB Audigy 1 [SB0092] (rev.3, serial:0x531102) at 0xd000, irq 20


Wyświetl listę urządzeń typu audio
aplay -l


**** List of PLAYBACK Hardware Devices ****
card 0: Intel [HDA Intel], device 0: ALC888 Analog [ALC888 Analog]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 0: Intel [HDA Intel], device 1: ALC888 Digital [ALC888 Digital]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: Audigy [SB Audigy 1 [SB0092]], device 0: emu10k1 [ADC Capture/Standard PCM Playback]
Subdevices: 32/32
Subdevice #0: subdevice #0
Subdevice #1: subdevice #1
Subdevice #2: subdevice #2
Subdevice #3: subdevice #3
Subdevice #4: subdevice #4
Subdevice #5: subdevice #5
Subdevice #6: subdevice #6
Subdevice #7: subdevice #7
Subdevice #8: subdevice #8
Subdevice #9: subdevice #9
Subdevice #10: subdevice #10
Subdevice #11: subdevice #11
Subdevice #12: subdevice #12
Subdevice #13: subdevice #13
Subdevice #14: subdevice #14
Subdevice #15: subdevice #15
Subdevice #16: subdevice #16
Subdevice #17: subdevice #17
Subdevice #18: subdevice #18
Subdevice #19: subdevice #19
Subdevice #20: subdevice #20
Subdevice #21: subdevice #21
Subdevice #22: subdevice #22
Subdevice #23: subdevice #23
Subdevice #24: subdevice #24
Subdevice #25: subdevice #25
Subdevice #26: subdevice #26
Subdevice #27: subdevice #27
Subdevice #28: subdevice #28
Subdevice #29: subdevice #29
Subdevice #30: subdevice #30
Subdevice #31: subdevice #31
card 1: Audigy [SB Audigy 1 [SB0092]], device 2: emu10k1 efx [Multichannel Capture/PT Playback]
Subdevices: 8/8
Subdevice #0: subdevice #0
Subdevice #1: subdevice #1
Subdevice #2: subdevice #2
Subdevice #3: subdevice #3
Subdevice #4: subdevice #4
Subdevice #5: subdevice #5
Subdevice #6: subdevice #6
Subdevice #7: subdevice #7
card 1: Audigy [SB Audigy 1 [SB0092]], device 3: emu10k1 [Multichannel Playback]
Subdevices: 1/1
Subdevice #0: subdevice #0


Wyświetl urządzenia PCI ze szczegółami
lspci -vvv


05:00.2 FireWire (IEEE 1394): Creative Labs SB Audigy FireWire Port (prog-if 10 [OHCI])
Subsystem: Creative Labs SB Audigy FireWire Port
Flags: bus master, medium devsel, latency 32, IRQ 19
Memory at fa005000 (32-bit, non-prefetchable) [size=2K]
Memory at fa000000 (32-bit, non-prefetchable) [size=16K]
Capabilities: [44] Power Management version 2
Kernel driver in use: firewire_ohci
Kernel modules: firewire-ohci

05:01.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/8139C/8139C+ (rev 10)
Subsystem: Edimax Computer Co. Device 9503
Flags: bus master, medium devsel, latency 64, IRQ 19
I/O ports at d200 [size=256]
Memory at fa004000 (32-bit, non-prefetchable) [size=256]
Capabilities: [50] Power Management version 2
Kernel driver in use: 8139too
Kernel modules: 8139too, 8139cp

05:02.0 Multimedia video controller: Conexant Systems, Inc. CX23880/1/2/3 PCI Video and Audio Decoder (rev 03)
Flags: bus master, medium devsel, latency 32, IRQ 18
Memory at f9000000 (32-bit, non-prefetchable) [size=16M]
Capabilities: [44] Vital Product Data
Capabilities: [4c] Power Management version 2
Kernel driver in use: cx8800
Kernel modules: cx8800

Linux - jak sprawdzić otwarte porty

środa, 23 lutego 2011 0 komentarze

W celu sprawdzenia otwartych portów należy wykonać komendę:

netstat -tulpn


Znaczenie opcji:
- t - porty TCP/IP
- u - porty UDP
- l - tylko porty typu LISTEN
- p - pokaż PID procesu związanego z otwartym portem
- n -pokazuje adresy numeryczne; nie próbuje zdeterminować symbolicznego hosta, portu czy nazwy użytkownika

FEDORA CORE 14 Linux - Konfiguracja karty TV: PixelView PlayTV HD

, wtorek, 15 lutego 2011 0 komentarze

Nadszedł czas na skonfigurowanie karty telewizyjnej na mojej Fedorze. Niestety na linux-ie konfiguracja nie jest tak prosta jak na Windozie i trochę czasu trzeba zaplanować żeby ten temat załatwić na swoim kompie wyposarzonym w linux-a. Mam nadzieję, że to co udało mi się zrobić i opisać niżej pomoże innym w konfiguracji karty TV i pozwoli na zaoszczędzenie czasu na Googlowanie w nadzieji, że coś się uda znaleźć żeby działało.

Opiszę tutaj moją konfigurację dla karty TV, którą aktualnie posiadam, czyli PixelView PlayTV HD opartej na chipie CX2388x.

Po pierwsze należy pobrać bieżącą wersję pliku pci.ids z dystrybucji i zainstalować co robimy komendą:

sudo update-pciids

Następnie sprawdzamy czy karta TV jest rozpoznawana przez system wyświetlając wszystkie podłączone urządzenia PCI:
lspci

U mnie jest to następujący wpis w liście wyświetlonej po wykonaniu komendy:
05:02.0 Multimedia video controller: Conexant Systems, Inc. CX23880/1/2/3 PCI Video and Audio Decoder (rev 03)
Teraz sprawdzamy czy w systemie jest załadowany odpowiedni sterownik w postaci modułu video4linux. W celu sprawdzenia czy odpowiedni sterownik jest załadowany w systemie należy wykonać komendę:
dmesg | grep cx88

Po wykonaniu tej komendy pewnie na ekranie pojawiła się lista na której końcu są następujące linie:

[ 11.307744] cx88[0]: subsystem: 0000:0000, board: UNKNOWN/GENERIC [card=0,autodetected], frontend(s): 0
[ 11.307747] cx88[0]: TV tuner type -1, Radio tuner type -1
[ 12.636927] tuner 1-0060: chip found @ 0xc0 (cx88[0])
[ 12.663271] cx88[0]/0: found at 0000:05:02.0, rev: 3, irq: 18, latency: 32, mmio: 0xf9000000
[ 12.663464] cx88[0]/0: registered device video0 [v4l2]
[ 12.663539] cx88[0]/0: registered device vbi0

Oznacza to, że system nie wykrył automatycznie typu karty i trzeba mu podpowiedzieć jaką mamy kartę i jej ustawienia. Teraz zaczyna się zwykle droga przez mękę polegająca na określeniu jaki to rodzaj karty posiadamy i wykonujemy to medodą prób i błędów wykonując następującą sekwencję komend:

sudo rmmod cx8800
sudo rmmod cx88xx
sudo modprobe cx88xx card=cx tuner=tx
sudo modprobe cx8800

gdzie cx - oznacza model karty TV, a tx - oznacza model tunera TV (lista typów kart i tunerów podana jest niżej).
W przypadku karty, której dotyczy opis ustawienia które powinny działać są następujące:
sudo modprobe cx88xx card=3 tuner=5 radio=5

Teraz przydało by się sprawdzić czy to działa. Po pierwsze wykonujemy komendę:
dmesg | grep cx88

co powinno nam dać w rezultacie mniej więcej następujący wynik:

[ 1358.792181] cx88/0: cx2388x v4l2 driver version 0.0.8 loaded
[ 1358.792311] cx8800 0000:05:02.0: PCI INT A -> GSI 18 (level, low) -> IRQ 18
[ 1358.795258] cx88[0]: subsystem: 0000:0000, board: PixelView [card=3,insmod option], frontend(s): 0
[ 1358.795261] cx88[0]: TV tuner type 5, Radio tuner type -1
[ 1358.911478] tuner 1-0060: chip found @ 0xc0 (cx88[0])
[ 1358.930185] cx88[0]/0: found at 0000:05:02.0, rev: 3, irq: 18, latency: 32, mmio: 0xf9000000
[ 1358.930306] cx88[0]/0: registered device video0 [v4l2]
[ 1358.930345] cx88[0]/0: registered device vbi0
[ 1358.930382] cx88[0]/0: registered device radio0

Następnie musimy sobie zainstalować program do oglądania telewizji. Ja używam w tym celu programu tvtime, na podstawie którego oprę dalszy opis.
No dobrze jeżeli mamy zainstalowany tvtime dobrze jest wykonać komendę:
tvtime-scanner

Komenda ta pozwala na wyszukanie kanałów po czym to co zostanie znalezione jest zapisywane w następującym pliku /home/myhome/.tvtime/stationlist.xml. W moim przypadku w wyniku działania przeszukania kanałów otrzymałem następującą zawartość pliku:













Oczywiście skaner nie wpisuje nazw kanałów to po wyp¶óbowaniu należy sobie uzupełnić samemu. Dodatkowo skaner może wyszukać kanały kilkukrotnie wtedy usunięcie polega na usunięciu odpowienich linii z pliku. Kolejność kanałów określa parametr position.
Dobrze teraz włączamy program tvtime. Po włączeniu powinniśmy już widzieć obraz z kanałów, jeżeli nie widać nic i jest niebieski obraz należy najprawdopodomniej zmienić źródło. Robimy to naciskając prawy klawisz myszy i dalej Ustawienia wejścia -> Zmień Źródło obrazu. U mnie jest to wartość Television. Można też dowolnie zmianiać inne ustawienia tej sekcji - proponuję popróbować lub poszukać na sieci co oznaczają poszczególne ustawienia.

Dobrze, najprawdopodobniej jest tak że mamy obraz i niestety nie mamy dźwięku. Tutaj następne schody. Ja mam u siebie podłączoną kartę telewizyjną bezpośrednio kablem do karty dźwiękowej czyli tzw. podłączenie typu "internal audio cable" i tutaj skupię się na przepisie jak uzyskać dźwięk w tego typu konfiguracji. Należy w tym przypadku wejść w ustawienia ALSA poprzez uruchomienie komendy:
alsamixer

Następnie zmieniamy kartę na odpowiednią (ja mam dwie karty dźwiekowe) używając klawisza funkcyjnego . Teraz przechodząc strzałkami kursora lokalizujemy Aux i zwiększamy głośność np. do 100% (klawisz kursora w górę). Najprawdopodoniej ten kanał jest również wyciszony, więc naciskamy klawisz . Przechodzimy dalej klawiszem kursora (w prawo) do kanału Analog M i zwiększamy głośność odpowiednio. U mnie wykonanie tych czynności spowodowało pojawienie się dźwięku. Z alsamixer wychodzimy przy użyciu .

Teraz wszystko powinno być OK, natomiast nie chcemy po każdym logowaniu wykonywać komend w celu aktywacji modułów i wprowadzania ustawień, dlatego należy w katalogu /etc/modprobe.d utworzyć plik z rozszerzeniem .conf np. tv.conf i wpisać do niego następującą zawartość:

alias char-major-89-* i2c-dev
options i2c-algo-bit bit_test=1

alias char-major-81 videodev
alias char-major-81-0 cx88xx
options cx88xx card=3 tuner=23 radio=23 i2c_scan=1 nicam=1 dsp_debug=1 always_analog=1 core_debug=1
alias char-major-81-0 cx8800
options cx8800 video_debug=1

Znaczenie poszczególnych wpisów proponuję poszukać sobie na stronach internetowych, ogólnie natomiast rzecz biorąc powyższe wpisy powodują odpowiednie załadowanie modułów oraz wprowadzenie odpowiednich ustawień dla tych modułów. W celu uzyskania informacji jakie parametry są dopuszczalne dla poszczególnych modułów proponuję użyć komendy modinfo np.
modinfo cx88xx


Należy pamiętać, że przed wprowadzeniem automatycznego ładowania modułów i ustawienia opcji dla nich najlepiej wykonać próby przy uzyciu rmmod i modprobe to może ustrzec od kłopotów na starcie systemu.


Lista typów kart:

1 -> Hauppauge WinTV 34xxx models [0070:3400,0070:3401]
2 -> GDI Black Gold [14c7:0106,14c7:0107]
3 -> PixelView [1554:4811]
4 -> ATI TV Wonder Pro [1002:00f8]
5 -> Leadtek Winfast 2000XP Expert [107d:6611,107d:6613]
6 -> AverTV Studio 303 (M126) [1461:000b]
7 -> MSI TV-@nywhere Master [1462:8606]
8 -> Leadtek Winfast DV2000 [107d:6620]
9 -> Leadtek PVR 2000 [107d:663b,107d:663c,107d:6632]
10 -> IODATA GV-VCP3/PCI [10fc:d003]
11 -> Prolink PlayTV PVR
12 -> ASUS PVR-416 [1043:4823,1461:c111]
13 -> MSI TV-@nywhere
14 -> KWorld/VStream XPert DVB-T [17de:08a6]
15 -> DViCO FusionHDTV DVB-T1 [18ac:db00]
16 -> KWorld LTV883RF
17 -> DViCO FusionHDTV 3 Gold-Q [18ac:d810,18ac:d800]
18 -> Hauppauge Nova-T DVB-T [0070:9002,0070:9001,0070:9000]
19 -> Conexant DVB-T reference design [14f1:0187]
20 -> Provideo PV259 [1540:2580]
21 -> DViCO FusionHDTV DVB-T Plus [18ac:db10,18ac:db11]
22 -> pcHDTV HD3000 HDTV [7063:3000]
23 -> digitalnow DNTV Live! DVB-T [17de:a8a6]
24 -> Hauppauge WinTV 28xxx (Roslyn) models [0070:2801]
25 -> Digital-Logic MICROSPACE Entertainment Center (MEC) [14f1:0342]
26 -> IODATA GV/BCTV7E [10fc:d035]
27 -> PixelView PlayTV Ultra Pro (Stereo)
28 -> DViCO FusionHDTV 3 Gold-T [18ac:d820]
29 -> ADS Tech Instant TV DVB-T PCI [1421:0334]
30 -> TerraTec Cinergy 1400 DVB-T [153b:1166]
31 -> DViCO FusionHDTV 5 Gold [18ac:d500]
32 -> AverMedia UltraTV Media Center PCI 550 [1461:8011]
33 -> Kworld V-Stream Xpert DVD
34 -> ATI HDTV Wonder [1002:a101]
35 -> WinFast DTV1000-T [107d:665f]
36 -> AVerTV 303 (M126) [1461:000a]
37 -> Hauppauge Nova-S-Plus DVB-S [0070:9201,0070:9202]
38 -> Hauppauge Nova-SE2 DVB-S [0070:9200]
39 -> KWorld DVB-S 100 [17de:08b2]
40 -> Hauppauge WinTV-HVR1100 DVB-T/Hybrid [0070:9400,0070:9402]
41 -> Hauppauge WinTV-HVR1100 DVB-T/Hybrid (Low Profile) [0070:9800,0070:9802]
42 -> digitalnow DNTV Live! DVB-T Pro [1822:0025,1822:0019]
43 -> KWorld/VStream XPert DVB-T with cx22702 [17de:08a1,12ab:2300]
44 -> DViCO FusionHDTV DVB-T Dual Digital [18ac:db50,18ac:db54]
45 -> KWorld HardwareMpegTV XPert [17de:0840]
46 -> DViCO FusionHDTV DVB-T Hybrid [18ac:db40,18ac:db44]
47 -> pcHDTV HD5500 HDTV [7063:5500]
48 -> Kworld MCE 200 Deluxe [17de:0841]
49 -> PixelView PlayTV P7000 [1554:4813]
50 -> NPG Tech Real TV FM Top 10 [14f1:0842]
51 -> WinFast DTV2000 H [107d:665e]
52 -> Geniatech DVB-S [14f1:0084]
53 -> Hauppauge WinTV-HVR3000 TriMode Analog/DVB-S/DVB-T [0070:1404,0070:1400,0070:1401,0070:1402]
54 -> Norwood Micro TV Tuner
55 -> Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM [c180:c980]
56 -> Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder [0070:9600,0070:9601,0070:9602]


Lista typów tunerów:

tuner=0 - Temic PAL (4002 FH5)
tuner=1 - Philips PAL_I (FI1246 and compatibles)
tuner=2 - Philips NTSC (FI1236,FM1236 and compatibles)
tuner=3 - Philips (SECAM+PAL_BG) (FI1216MF, FM1216MF, FR1216MF)
tuner=4 - NoTuner
tuner=5 - Philips PAL_BG (FI1216 and compatibles)
tuner=6 - Temic NTSC (4032 FY5)
tuner=7 - Temic PAL_I (4062 FY5)
tuner=8 - Temic NTSC (4036 FY5)
tuner=9 - Alps HSBH1
tuner=10 - Alps TSBE1
tuner=11 - Alps TSBB5
tuner=12 - Alps TSBE5
tuner=13 - Alps TSBC5
tuner=14 - Temic PAL_BG (4006FH5)
tuner=15 - Alps TSCH6
tuner=16 - Temic PAL_DK (4016 FY5)
tuner=17 - Philips NTSC_M (MK2)
tuner=18 - Temic PAL_I (4066 FY5)
tuner=19 - Temic PAL* auto (4006 FN5)
tuner=20 - Temic PAL_BG (4009 FR5) or PAL_I (4069 FR5)
tuner=21 - Temic NTSC (4039 FR5)
tuner=22 - Temic PAL/SECAM multi (4046 FM5)
tuner=23 - Philips PAL_DK (FI1256 and compatibles)
tuner=24 - Philips PAL/SECAM multi (FQ1216ME)
tuner=25 - LG PAL_I+FM (TAPC-I001D)
tuner=26 - LG PAL_I (TAPC-I701D)
tuner=27 - LG NTSC+FM (TPI8NSR01F)
tuner=28 - LG PAL_BG+FM (TPI8PSB01D)
tuner=29 - LG PAL_BG (TPI8PSB11D)
tuner=30 - Temic PAL* auto + FM (4009 FN5)
tuner=31 - SHARP NTSC_JP (2U5JF5540)
tuner=32 - Samsung PAL TCPM9091PD27
tuner=33 - MT20xx universal
tuner=34 - Temic PAL_BG (4106 FH5)
tuner=35 - Temic PAL_DK/SECAM_L (4012 FY5)
tuner=36 - Temic NTSC (4136 FY5)
tuner=37 - LG PAL (newer TAPC series)
tuner=38 - Philips PAL/SECAM multi (FM1216ME MK3)
tuner=39 - LG NTSC (newer TAPC series)
tuner=40 - HITACHI V7-J180AT
tuner=41 - Philips PAL_MK (FI1216 MK)
tuner=42 - Philips 1236D ATSC/NTSC dual in
tuner=43 - Philips NTSC MK3 (FM1236MK3 or FM1236/F)
tuner=44 - Philips 4 in 1 (ATI TV Wonder Pro/Conexant)
tuner=45 - Microtune 4049 FM5
tuner=46 - Panasonic VP27s/ENGE4324D
tuner=47 - LG NTSC (TAPE series)
tuner=48 - Tenna TNF 8831 BGFF)
tuner=49 - Microtune 4042 FI5 ATSC/NTSC dual in
tuner=50 - TCL 2002N
tuner=51 - Philips PAL/SECAM_D (FM 1256 I-H3)
tuner=52 - Thomson DTT 7610 (ATSC/NTSC)
tuner=53 - Philips FQ1286
tuner=54 - tda8290+75
tuner=55 - TCL 2002MB
tuner=56 - Philips PAL/SECAM multi (FQ1216AME MK4)
tuner=57 - Philips FQ1236A MK4
tuner=58 - Ymec TVision TVF-8531MF/8831MF/8731MF
tuner=59 - Ymec TVision TVF-5533MF
tuner=60 - Thomson DTT 761X (ATSC/NTSC)
tuner=61 - Tena TNF9533-D/IF/TNF9533-B/DF
tuner=62 - Philips TEA5767HN FM Radio
tuner=63 - Philips FMD1216ME MK3 Hybrid Tuner
tuner=64 - LG TDVS-H06xF
tuner=65 - Ymec TVF66T5-B/DFF
tuner=66 - LG TALN series
tuner=67 - Philips TD1316 Hybrid Tuner
tuner=68 - Philips TUV1236D ATSC/NTSC dual in
tuner=69 - Tena TNF 5335 and similar models
tuner=70 - Samsung TCPN 2121P30A
tuner=71 - Xceive xc3028
tuner=72 - Thomson FE6600
tuner=73 - Samsung TCPG 6121P30A



-

FEDORA CORE 14 Linux - Konfiguracja L2TP/IPsec

, piątek, 11 lutego 2011 0 komentarze

Chciałbym przedstawić sposób na skonfigurowanie połączenia L2TP/IPsec w Fedorze. Trochę się z tym tematem ostatnio pomęczyłem i jeżeli kouś to pomoże to przedstawię tutaj procedurę "krok po kroku" jak to wykonać.

No to zacznijmy od początku, czyli od ściągnięcia odpowiednich pakietów, czyli:
1. OpenSWAN
2. xl2tpd

Następnie zabieramy się za przygotowanie klucza i bazy kluczy NSS. Zakładam, że w posiadaniu jest klucz w postaci pkcs12, jeżeli nie a mamy 2 certyfikaty i klucz prywatny w osobnych plikach tekstowych (format PEM) to należy wykonać następującą komendę do stworzenia klucza pkcs12:

openssl pkcs12 -export -in cert.pem -inkey user.key -certfile cacert.pem -out certkey.p12 -name 'masl-12'

Oczywiście w powyższej instrukcji można użyć dowolnej nazwy, w tym przypadku należy zmianić 'masl-12' na dowolnie wybrany ciąg znaków. Po naciśnięciu w terminalu nastąpi prośba o podanie hasła do klucza prywatnego ("Enter pass phrase for user.key:"), które to hasło należy mieć i w tym momencie wpisać. Po wpisaniu poprawnym hasła powinno nastąpić pytanie o hasło exportu ("Enter Export Password:") tutaj wpisujemy dowolne hasło i po potwierdzamy wpisując je ponownie. Po wykonaniu powyżej opisanych króków powinein zostać utworzony w bieżącym katalogu plik o nazwie 'certkey.p12'.

Teraz plik z danymi PKCS#12 utworzony w poprzednim kroku należy zaimportować do bazy kluczy i certyfikatów. Wcześniej jednak musimy sobie stworzyć nową bazę (lub raczej nowe bazy) i robimy to przy pomocy następujacej komendy:
su -c "certutil -N -d /etc/ipsec.d"

Po jej wywołaniu zostaniemy poproszeni o podanie hasła ROOT-a (wpisujemy odpowiednie hasło) a następnie o nowe hasło i jego powtórzenie ("Enter new password:", "Re-enter password:") do nowotworzonej bazy kluczy. Można to hasło pozostawić puste poprzez wykonanie bez podawania czegokolwiek. Po tej operacji powinny zostać utworzone następujące pliki w katalogu '/etc/ipsec.d': cert8.db, key3.db, secmod.db.

Teraz import pliku z danymi PKCS#12 do nowoutworzonej bazy kluczy, wykonujemy następującą komendą:
su -c "pk12util -i certkey.p12 -d /etc/ipsec.d"

W trakcie wywołania powyższej komendy zostaniemy poproszeni o hasło ROOT-a, a następnie o hasło które wprowadziliśmy przy eksporcie. Jeżeli wszystko uda ło się zrobić tak jak trzeba powinien być na terminalu wpis pk12util: PKCS12 IMPORT SUCCESSFUL.

Super udało nam się zaimportować do bazy zawartość pliku z danymi PKCS#12 - przynajmniej wszystko na to wskazuje, ale sprawdźmy. Możemy to zrobić wykorzystując komendę:
su -c "certutil -L -d /etc/ipsec.d"

No dobrze wszystko gra w takim razie przechodzimy do konfiguracji IPSEC. Po pierwsze edytujemy plik /etc/ipsec/ipsec.secrets poprzez dodanie w nim następującej linii:
: RSA "masl-12" "[hasło]"

gdzie "masl-12" oznacza nazwę klucza wprowadzoną przy eksporcie przy użyciu 'openssl', a w miejsce [hasło] należy wpisać hasło w naszym przypadku jest to pusta wartość. Zapisujemy wyedytowany plik.

Co dalej. Dalej przechodzimy do edycji pliku /etc/ipsec/ipsec.conf i tutaj usuwamy to co jest już tam wpisane i zaczyna się od 'config setup' a następnie wpisujemy to co poniżej:
config setup
nat_traversal=yes
interfaces=%defaultroute
protostack=netkey
#plutodebug=all
#plutostderrlog=/tmp/pluto.log

conn IPSEC-conn
authby=rsasig
pfs=no
rekey=yes
keyingtries=3
type=transport
left=0.0.0.0 ; <- tutaj wpisać lokalne IP (ifconfig)
leftid="C=PL, ST=Malopolska, L=Cracow, O=BleBle, CN=Marcin Slowik, E=masl@xxxx.pl"
leftcert="masl-12" ; <- nazwa certyfikatu wprowadzona w kroku z komendą openssl
leftrsasigkey=%cert
leftprotoport=17/1701
right=
rightrsasigkey=%cert
rightca=%same
rightid=@xx.yyyyy.zz
rightprotoport=17/1701
auto=add

Bardzo ważną sprawą jest podanie jako 'leftid' identyfikatora w formie DN (distinguished name) - gdy tego się nie poda przedstawimy się serwerowi w formie adresu IP i możemy mieć problem 'no RSA no RSA public key known for 0.0.0.0' na serwerze a na kliencie INVALID_KEY_INFORMATION. Pytanie skąd wziąć tą informację - odpowiedź - wykonać:
su -c "certutil -L -d /etc/ipsec.d -n masl-12"

i skopiować wartość z pola 'Subject:'. Ważne jest to że w przypadku, gdy po wykonaniu komedy 'ipsec auto --up IPSEC-conn' (opisane w dalszej części) dostaniemy błąd 'INVALID_KEY_INFORMATION' należy skopiowane wartości odwrócić, czyli przekształcić z formy
E=masl@xxxx.pl,CN=Marcin Slowik,O=BleBle,L=Cracow,ST=Malopolska,C=PL
na formę
C=PL, ST=Malopolska, L=Cracow, O=BleBle, CN=Marcin Slowik, E=masl@xxxx.pl
. Jako 'left' należy podać lokalny adres sieciowy maszyny z której się łączymy, czyli jeżeli jesteśmy w sieci lokalnej np. 192.168.1.2. Można uzyć zamiast adresu IP wartości '%defaultroute' jednak polecam wartość IP na początek i jak wszystko zacznie działać to wprowadzenie '%defaultroute' ponieważ może to być przyczyną niepotrzebnych poszukiwań co jest nie tak. Jako 'right' należy podać adres IP lub nazwę hosta docelowego / serwera - polecam przy pierwszej próbie wpisać IP potem ewentualnie nazwę (tutaj też czasem są problemy których przy pierwszym podejściu proponuję unikać). Jako 'rightid' należy wpisać po małpce FQDN (Fully Qualified Domain Name) serwera.

No dobra zróbmy pierwszą próbę czy wszystko działa w miarę dobrze. Zróbmy następującą sekwencję komend:

su
service ipsec stop
service ipsec start

Po takiej sekwencji powinniśmy dostać po wykonaniu:
tail -f /var/log/secure

mniej więcej coś takiego na końcu:
Feb 16 22:34:00 fedora pluto[3874]: loaded private key for keyid: PPK_RSA:DwBnRRRR

Ważne jest jeszcze, żeby sprawdzić czy zostało dodane połączenie o nazwie 'IPSEC-conn', czyli sprawdzenie czy w pliku '/var/log/secure' znajduje się coś w tym stylu:
Feb 16 22:34:00 fedora pluto[3874]: added connection description "IPSEC-conn"

Teraz sprawdzamy poprawność połączenia i wymiany kluczy, czyli wykonujemy z linii komend:
ipsec auto --up IPSEC-conn


Jeżeli wszystko działa poprawnie powinniśmy dostać na konsoli coś w tym stylu:
117 "IPSEC-conn" #2: STATE_QUICK_I1: initiate
004 "IPSEC-conn" #2: STATE_QUICK_I2: sent QI2, IPsec SA established transport mode {ESP=>............}

No dobra jeżeli działa to przechodzimy do następnego kroku, czyli konfiguracji xl2tp. Konfiguracja xl2tp polega na modyfikacji trzech plików. Pierwszy to /etc/ppp/chap-secrets. W tym pliku dodajemy następującą linię:

test * test *

pierwsze 'test' to user-name drugie 'test' to hasło, czyli informacje potrzebne do podstawowej autentykacji połączenia z serwerem.

Następny plik to /etc/ppp/options.l2tpd.con. Ten plik tworzymy i wypełniamy następującą treścią:

ipcp-accept-local
ipcp-accept-remote
refuse-eap
noccp
noauth
crtscts
idle 1800
mtu 1410
mru 1410
nodefaultroute
debug
lock
connect-delay 5000

No i ostatni z plików to /etc/xl2tpd/xl2tpd.conf, w którym dodajemy to co następuje:

[global]
listen-addr = 192.168.1.2

[lac L2TPS]
lns = [FQDN for serwer]
require chap = yes
refuse pap = yes
require authentication = yes
name = test
ppp debug = yes
pppoptfile = /etc/ppp/options.l2tpd.con
length bit = yes


No dobra to w takim razie odpalamy xl2tpd:
service xl2tpd start

Jeżeli wszystko ok powinniśmy mieć w pliku /var/log/messages wpis:


Feb 16 23:29:06 fedora xl2tpd[4955]: Listening on IP address 192.168.1.2, port 1701

No dobra teraz odpalmy połączenie, czyli do wykonania są następujące komendy:

ipsec auto --up IPSEC-conn
echo "c L2TPS" > /var/run/xl2tpd/l2tp-control


Jeżeli po wykonaniu komend powyżej dostałeś w pliku /var/log/messages coś takiego:
Feb 17 00:40:31 fedora pppd[9709]: Warning: can't open options file /root/.ppprc: Permission denied

to proponuję przejść do ustawień polityki selinux raczej na pewno nie są to inne uprawnienia.

No dobrze dalej jak mamy połączenie to musimy zdefiniować routing dla adresów z podsieci na serwerze, ponieważ domyślnie mamy dostępny jeden adres. Można to zrobić np. dla całej puli adresów sieci prywatnej zaczynających się od 10. w sposób podany niżej:

ip route add 10.0.0.0/8 dev ppp0


Można jeszcze na przykład dodać name server, który znajduje się w sieci prywatnej na adresie 10.5.1.1 w sposób podany poniżej:

echo "nameserver 10.5.1.1" >> /etc/resolv.conf


Zamykanie wszystkiego po kolei to wykonanie skryptu:

ip route del 10.0.0.0
echo "d L2TPS" > /var/run/xl2tpd/l2tp-control
ipsec auto --down IPSEC-conn
service xl2tpd stop
service ipsec stop


To wszystko na ten temat. Mam nadzieję, że to komuś pomoże w konfiguracji połączenia VPN.

GlossyBlue Blogger by Black Quanta. Theme & Icons by N.Design Studio
Entries RSS Comments RSS